├── README.md
├── demo
├── 3. Flutter布局
│ └── flutter_layout_demo
│ │ ├── .gitignore
│ │ ├── .metadata
│ │ ├── README.md
│ │ ├── android
│ │ ├── .gitignore
│ │ ├── app
│ │ │ ├── build.gradle
│ │ │ └── src
│ │ │ │ └── main
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── loki
│ │ │ │ │ └── flutterlayoutdemo
│ │ │ │ │ └── MainActivity.java
│ │ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ ├── build.gradle
│ │ ├── gradle.properties
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ └── gradle-wrapper.properties
│ │ └── settings.gradle
│ │ ├── assets
│ │ └── images
│ │ │ ├── 2.0x
│ │ │ ├── avatar2.png
│ │ │ ├── nav_close.png
│ │ │ ├── publish_chat_box.png
│ │ │ ├── publish_work_line.png
│ │ │ ├── publish_work_sign.png
│ │ │ ├── share_qq.png
│ │ │ └── share_wechat.png
│ │ │ ├── 3.0x
│ │ │ ├── avatar2.png
│ │ │ ├── nav_close.png
│ │ │ ├── publish_chat_box.png
│ │ │ ├── publish_work_line.png
│ │ │ ├── publish_work_sign.png
│ │ │ ├── share_qq.png
│ │ │ └── share_wechat.png
│ │ │ ├── avatar2.png
│ │ │ ├── nav_close.png
│ │ │ ├── publish_chat_box.png
│ │ │ ├── publish_work_line.png
│ │ │ ├── publish_work_sign.png
│ │ │ ├── share_qq.png
│ │ │ └── share_wechat.png
│ │ ├── ios
│ │ ├── .gitignore
│ │ ├── Flutter
│ │ │ ├── AppFrameworkInfo.plist
│ │ │ ├── Debug.xcconfig
│ │ │ └── Release.xcconfig
│ │ ├── Runner.xcodeproj
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace
│ │ │ │ └── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ └── xcschemes
│ │ │ │ └── Runner.xcscheme
│ │ ├── Runner.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── Runner
│ │ │ ├── AppDelegate.h
│ │ │ ├── AppDelegate.m
│ │ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ │ └── LaunchImage.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ └── README.md
│ │ │ ├── Base.lproj
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ │ ├── Info.plist
│ │ │ └── main.m
│ │ ├── lib
│ │ ├── main.dart
│ │ └── sample_page.dart
│ │ ├── pubspec.yaml
│ │ └── test
│ │ └── widget_test.dart
└── 4. Flutter布局Widget介绍
│ └── flutter_layout_demo
│ ├── .gitignore
│ ├── .metadata
│ ├── README.md
│ ├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── study
│ │ │ │ └── flutterlayoutdemo
│ │ │ │ └── MainActivity.java
│ │ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
│ ├── images
│ └── pic.jpg
│ ├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── Runner
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
│ ├── lib
│ ├── helper
│ │ └── ListViewBuilder.dart
│ ├── main.dart
│ ├── multi
│ │ ├── Column.dart
│ │ ├── CustomMultiChildLayout.dart
│ │ ├── Flow.dart
│ │ ├── GridView.dart
│ │ ├── IndexedStack.dart
│ │ ├── ListBody.dart
│ │ ├── ListView.dart
│ │ ├── Row.dart
│ │ ├── Stack.dart
│ │ ├── Table.dart
│ │ └── Wrap.dart
│ └── single
│ │ ├── Align.dart
│ │ ├── AspectRatio.dart
│ │ ├── Baseline.dart
│ │ ├── Center.dart
│ │ ├── ConstrainedBox.dart
│ │ ├── Container.dart
│ │ ├── CustomSingleChildLayout.dart
│ │ ├── FittedBox.dart
│ │ ├── FractionallySizedBox.dart
│ │ ├── IntrinsicHeight.dart
│ │ ├── IntrinsicWidth.dart
│ │ ├── LimitedBox.dart
│ │ ├── Offstage.dart
│ │ ├── OverflowBox.dart
│ │ ├── Padding.dart
│ │ ├── SizedBox.dart
│ │ ├── SizedOverflowBox.dart
│ │ └── Transform.dart
│ ├── pubspec.yaml
│ └── test
│ └── widget_test.dart
└── post
├── 1. Flutter 不一样的跨平台解决方案.md
├── 10. Flutter 布局(六)- SizedOverflowBox、Transform、CustomSingleChildLayout详解.md
├── 11. Flutter 布局(七)- Row、Column详解.md
├── 12. Flutter 布局(八)- Stack、IndexedStack、GridView详解.md
├── 13. Flutter 布局(九)- Flow、Table、Wrap详解.md
├── 14. Flutter 布局(十)- ListBody、ListView、CustomMultiChildLayout详解.md
├── 15. Flutter 布局控件完结篇.md
├── 16. Flutter 动画详解(一).md
├── 17. Flutter 动画详解(二).md
├── 2. Flutter Plugin开发流程.md
├── 3. Flutter 布局详解.md
├── 4. Flutter 布局(一)- Container详解.md
├── 5. Flutter 布局(二)- Padding、Align、Center详解.md
├── 6. Flutter 布局(三)- FittedBox、AspectRatio、ConstrainedBox详解.md
├── 7. 在现有项目中添加Flutter.md
├── 8. Flutter 布局(四)- Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth详解.md
└── 9. Flutter 布局(五)- LimitedBox、Offstage、OverflowBox、SizedBox详解.md
/README.md:
--------------------------------------------------------------------------------
1 | # flutter-study
2 |
3 | 会持续更新Flutter学习过程中的总结,会在这里发布Flutter相关的文章。
4 |
5 | 其中post里面包含发布的文章,使用markdown编写,demo中则是文章中的例子工程。欢迎star。
6 |
7 |
8 | ## 文章
9 |
10 | 1. [Flutter - 不一样的跨平台解决方案](https://github.com/yang7229693/flutter-study/blob/master/post/1.%20Flutter%20%E4%B8%8D%E4%B8%80%E6%A0%B7%E7%9A%84%E8%B7%A8%E5%B9%B3%E5%8F%B0%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88.md)
11 | 2. [Flutter Plugin开发流程](https://github.com/yang7229693/flutter-study/blob/master/post/2.%20Flutter%20Plugin%E5%BC%80%E5%8F%91%E6%B5%81%E7%A8%8B.md)
12 | 3. [Flutter 布局详解](https://github.com/yang7229693/flutter-study/blob/master/post/3.%20Flutter%20%E5%B8%83%E5%B1%80%E8%AF%A6%E8%A7%A3.md)
13 | 4. [现有项目中集成Flutter](https://github.com/yang7229693/flutter-study/blob/master/post/7.%20%E5%9C%A8%E7%8E%B0%E6%9C%89%E9%A1%B9%E7%9B%AE%E4%B8%AD%E6%B7%BB%E5%8A%A0Flutter.md)
14 | 5. [Flutter 布局(一)- Container详解](https://github.com/yang7229693/flutter-study/blob/master/post/4.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E4%B8%80%EF%BC%89-%20Container%E8%AF%A6%E8%A7%A3.md)
15 | 6. [Flutter 布局(二)- Padding、Align、Center详解](https://github.com/yang7229693/flutter-study/blob/master/post/5.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E4%BA%8C%EF%BC%89-%20Padding%E3%80%81Align%E3%80%81Center%E8%AF%A6%E8%A7%A3.md)
16 | 7. [Flutter 布局(三)- FittedBox、AspectRatio、ConstrainedBox详解](https://github.com/yang7229693/flutter-study/blob/master/post/6.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E4%B8%89%EF%BC%89-%20FittedBox%E3%80%81AspectRatio%E3%80%81ConstrainedBox%E8%AF%A6%E8%A7%A3.md)
17 | 8. [Flutter 布局(四)- Baseline、FractionallySizedBox、IntrinsicHeight、IntrinsicWidth详解](https://github.com/yang7229693/flutter-study/blob/master/post/8.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E5%9B%9B%EF%BC%89-%20Baseline%E3%80%81FractionallySizedBox%E3%80%81IntrinsicHeight%E3%80%81IntrinsicWidth%E8%AF%A6%E8%A7%A3.md)
18 | 9. [Flutter 布局(五)- LimitedBox、Offstage、OverflowBox、SizedBox详解](https://github.com/yang7229693/flutter-study/blob/master/post/9.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E4%BA%94%EF%BC%89-%20LimitedBox%E3%80%81Offstage%E3%80%81OverflowBox%E3%80%81SizedBox%E8%AF%A6%E8%A7%A3.md)
19 | 10. [Flutter 布局(六)- SizedOverflowBox、Transform、CustomSingleChildLayout详解](https://github.com/yang7229693/flutter-study/blob/master/post/10.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E5%85%AD%EF%BC%89-%20SizedOverflowBox%E3%80%81Transform%E3%80%81CustomSingleChildLayout%E8%AF%A6%E8%A7%A3.md)
20 | 11. [Flutter 布局(七)- Row、Column详解](https://github.com/yang7229693/flutter-study/blob/master/post/11.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E4%B8%83%EF%BC%89-%20Row%E3%80%81Column%E8%AF%A6%E8%A7%A3.md)
21 | 12. [Flutter 布局(八)- Stack、IndexedStack、GridView详解](https://github.com/yang7229693/flutter-study/blob/master/post/12.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E5%85%AB%EF%BC%89-%20Stack%E3%80%81IndexedStack%E3%80%81GridView%E8%AF%A6%E8%A7%A3.md)
22 | 13. [Flutter 布局(九)- Flow、Table、Wrap详解](https://github.com/yang7229693/flutter-study/blob/master/post/13.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E4%B9%9D%EF%BC%89-%20Flow%E3%80%81Table%E3%80%81Wrap%E8%AF%A6%E8%A7%A3.md)
23 | 14. [Flutter 布局(十)- ListBody、ListView、CustomMultiChildLayout详解](https://github.com/yang7229693/flutter-study/blob/master/post/14.%20Flutter%20%E5%B8%83%E5%B1%80%EF%BC%88%E5%8D%81%EF%BC%89-%20ListBody%E3%80%81ListView%E3%80%81CustomMultiChildLayout%E8%AF%A6%E8%A7%A3.md)
24 | 15. [Flutter 布局控件完结篇
25 | ](https://github.com/yang7229693/flutter-study/blob/master/post/15.%20Flutter%20%E5%B8%83%E5%B1%80%E6%8E%A7%E4%BB%B6%E5%AE%8C%E7%BB%93%E7%AF%87.md)
26 | 16. [Flutter 动画详解(一)](https://github.com/yang7229693/flutter-study/blob/master/post/16.%20Flutter%20%E5%8A%A8%E7%94%BB%E8%AF%A6%E8%A7%A3%EF%BC%88%E4%B8%80%EF%BC%89.md)
27 | 17. [Flutter 动画详解(二)](https://github.com/yang7229693/flutter-study/blob/master/post/17.%20Flutter%20%E5%8A%A8%E7%94%BB%E8%AF%A6%E8%A7%A3%EF%BC%88%E4%BA%8C%EF%BC%89.md)
28 |
29 |
30 | ## Demo
31 |
32 | 1. [Flutter 布局详解 Demo工程](https://github.com/yang7229693/flutter-study/tree/master/demo/3.%20Flutter%E5%B8%83%E5%B1%80)
33 | 2. [Flutter布局系列 Demo工程](https://github.com/yang7229693/flutter-study/tree/master/demo/4.%20Flutter%E5%B8%83%E5%B1%80Widget%E4%BB%8B%E7%BB%8D/flutter_layout_demo)
34 |
35 | ## 其他
36 |
37 | [个人博客](http://whysodiao.com)
38 |
39 | [掘金专栏](https://juejin.im/user/5ad0162df265da2397074520/posts)
40 |
41 | [简书主页](https://www.jianshu.com/u/312aad1f1c8b)
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | .flutter-plugins
10 |
11 | # Miscellaneous
12 | *.class
13 | *.lock
14 | *.log
15 | *.pyc
16 | *.swp
17 | .DS_Store
18 | .atom/
19 | .buildlog/
20 | .history
21 | .svn/
22 |
23 | # IntelliJ related
24 | *.iml
25 | *.ipr
26 | *.iws
27 | .idea/
28 |
29 | # Visual Studio Code related
30 | .vscode/
31 |
32 | # Flutter repo-specific
33 | /bin/cache/
34 | /bin/mingit/
35 | /dev/benchmarks/mega_gallery/
36 | /dev/bots/.recipe_deps
37 | /dev/bots/android_tools/
38 | /dev/docs/doc/
39 | /dev/docs/lib/
40 | /dev/docs/pubspec.yaml
41 | /packages/flutter/coverage/
42 | version
43 |
44 | # Flutter/Dart/Pub related
45 | **/doc/api/
46 | .dart_tool/
47 | .flutter-plugins
48 | .packages
49 | .pub-cache/
50 | .pub/
51 | build/
52 | flutter_*.png
53 | linked_*.ds
54 | unlinked.ds
55 | unlinked_spec.ds
56 |
57 | # Android related
58 | **/android/**/gradle-wrapper.jar
59 | **/android/.gradle
60 | **/android/captures/
61 | **/android/gradlew
62 | **/android/gradlew.bat
63 | **/android/local.properties
64 | **/android/**/GeneratedPluginRegistrant.java
65 |
66 | # iOS/XCode related
67 | **/ios/**/*.mode1v3
68 | **/ios/**/*.mode2v3
69 | **/ios/**/*.moved-aside
70 | **/ios/**/*.pbxuser
71 | **/ios/**/*.perspectivev3
72 | **/ios/**/*sync/
73 | **/ios/**/.sconsign.dblite
74 | **/ios/**/.tags*
75 | **/ios/**/.vagrant/
76 | **/ios/**/DerivedData/
77 | **/ios/**/Icon?
78 | **/ios/**/Pods/
79 | **/ios/**/.symlinks/
80 | **/ios/**/profile
81 | **/ios/**/xcuserdata
82 | **/ios/.generated/
83 | **/ios/Flutter/App.framework
84 | **/ios/Flutter/Flutter.framework
85 | **/ios/Flutter/Generated.xcconfig
86 | **/ios/Flutter/app.flx
87 | **/ios/Flutter/app.zip
88 | **/ios/Flutter/flutter_assets/
89 | **/ios/ServiceDefinitions.json
90 | **/ios/Runner/GeneratedPluginRegistrant.*
91 |
92 | # Exceptions to above rules.
93 | !**/ios/**/default.mode1v3
94 | !**/ios/**/default.mode2v3
95 | !**/ios/**/default.pbxuser
96 | !**/ios/**/default.perspectivev3
97 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 12bbaba9ae044d0ea77da4dd5e4db15eed403f09
8 | channel: beta
9 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/README.md:
--------------------------------------------------------------------------------
1 | # flutter_layout_demo
2 |
3 | A Flutter Layout Demo Application.
4 |
5 | ## Getting Started
6 |
7 | For help getting started with Flutter, view our online
8 | [documentation](https://flutter.io/).
9 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.class
3 | .gradle
4 | /local.properties
5 | /.idea/workspace.xml
6 | /.idea/libraries
7 | .DS_Store
8 | /build
9 | /captures
10 | GeneratedPluginRegistrant.java
11 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | apply plugin: 'com.android.application'
15 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
16 |
17 | android {
18 | compileSdkVersion 27
19 |
20 | lintOptions {
21 | disable 'InvalidPackage'
22 | }
23 |
24 | defaultConfig {
25 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
26 | applicationId "com.loki.flutterlayoutdemo"
27 | minSdkVersion 16
28 | targetSdkVersion 27
29 | versionCode 1
30 | versionName "1.0"
31 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
32 | }
33 |
34 | buildTypes {
35 | release {
36 | // TODO: Add your own signing config for the release build.
37 | // Signing with the debug keys for now, so `flutter run --release` works.
38 | signingConfig signingConfigs.debug
39 | }
40 | }
41 | }
42 |
43 | flutter {
44 | source '../..'
45 | }
46 |
47 | dependencies {
48 | testImplementation 'junit:junit:4.12'
49 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
50 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
51 | }
52 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
15 |
19 |
26 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/java/com/loki/flutterlayoutdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.loki.flutterlayoutdemo;
2 |
3 | import android.os.Bundle;
4 |
5 | import io.flutter.app.FlutterActivity;
6 | import io.flutter.plugins.GeneratedPluginRegistrant;
7 |
8 | public class MainActivity extends FlutterActivity {
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | GeneratedPluginRegistrant.registerWith(this);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.0.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/avatar2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/avatar2.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/nav_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/nav_close.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/publish_chat_box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/publish_chat_box.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/publish_work_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/publish_work_line.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/publish_work_sign.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/publish_work_sign.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/share_qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/share_qq.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/share_wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/2.0x/share_wechat.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/avatar2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/avatar2.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/nav_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/nav_close.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/publish_chat_box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/publish_chat_box.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/publish_work_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/publish_work_line.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/publish_work_sign.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/publish_work_sign.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/share_qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/share_qq.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/share_wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/3.0x/share_wechat.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/avatar2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/avatar2.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/nav_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/nav_close.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/publish_chat_box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/publish_chat_box.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/publish_work_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/publish_work_line.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/publish_work_sign.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/publish_work_sign.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/share_qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/share_qq.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/assets/images/share_wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/assets/images/share_wechat.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/app.flx
37 | /Flutter/app.zip
38 | /Flutter/flutter_assets/
39 | /Flutter/App.framework
40 | /Flutter/Flutter.framework
41 | /Flutter/Generated.xcconfig
42 | /ServiceDefinitions.json
43 |
44 | Pods/
45 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | UIRequiredDeviceCapabilities
24 |
25 | arm64
26 |
27 | MinimumOSVersion
28 | 8.0
29 |
30 |
31 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
7 | [GeneratedPluginRegistrant registerWithRegistry:self];
8 | // Override point for customization after application launch.
9 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
10 | }
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_layout_demo
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | arm64
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | UIViewControllerBasedStatusBarAppearance
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_layout_demo/sample_page.dart';
3 |
4 | void main() => runApp(new MyApp());
5 |
6 | class MyApp extends StatelessWidget {
7 | // This widget is the root of your application.
8 | @override
9 | Widget build(BuildContext context) {
10 | return new MaterialApp(
11 | title: 'Flutter Demo',
12 | theme: new ThemeData(
13 | // This is the theme of your application.
14 | //
15 | // Try running your application with "flutter run". You'll see the
16 | // application has a blue toolbar. Then, without quitting the app, try
17 | // changing the primarySwatch below to Colors.green and then invoke
18 | // "hot reload" (press "r" in the console where you ran "flutter run",
19 | // or press Run > Flutter Hot Reload in IntelliJ). Notice that the
20 | // counter didn't reset back to zero; the application is not restarted.
21 | primarySwatch: Colors.blue,
22 | ),
23 | home: new MyHomePage(title: 'Flutter Demo Home Page'),
24 | );
25 | }
26 | }
27 |
28 | class MyHomePage extends StatefulWidget {
29 | MyHomePage({Key key, this.title}) : super(key: key);
30 |
31 | // This widget is the home page of your application. It is stateful, meaning
32 | // that it has a State object (defined below) that contains fields that affect
33 | // how it looks.
34 |
35 | // This class is the configuration for the state. It holds the values (in this
36 | // case the title) provided by the parent (in this case the App widget) and
37 | // used by the build method of the State. Fields in a Widget subclass are
38 | // always marked "final".
39 |
40 | final String title;
41 |
42 | @override
43 | _MyHomePageState createState() => new _MyHomePageState();
44 | }
45 |
46 | class _MyHomePageState extends State {
47 | int _counter = 0;
48 |
49 | void _incrementCounter() {
50 | setState(() {
51 | // This call to setState tells the Flutter framework that something has
52 | // changed in this State, which causes it to rerun the build method below
53 | // so that the display can reflect the updated values. If we changed
54 | // _counter without calling setState(), then the build method would not be
55 | // called again, and so nothing would appear to happen.
56 | _counter++;
57 | });
58 | }
59 |
60 | @override
61 | Widget build(BuildContext context) {
62 | // This method is rerun every time setState is called, for instance as done
63 | // by the _incrementCounter method above.
64 | //
65 | // The Flutter framework has been optimized to make rerunning build methods
66 | // fast, so that you can just rebuild anything that needs updating rather
67 | // than having to individually change instances of widgets.
68 | return new Scaffold(
69 | appBar: new AppBar(
70 | // Here we take the value from the MyHomePage object that was created by
71 | // the App.build method, and use it to set our appbar title.
72 | title: new Text(widget.title),
73 | ),
74 | body: new Center(
75 | // Center is a layout widget. It takes a single child and positions it
76 | // in the middle of the parent.
77 | child: new Column(
78 | // Column is also layout widget. It takes a list of children and
79 | // arranges them vertically. By default, it sizes itself to fit its
80 | // children horizontally, and tries to be as tall as its parent.
81 | //
82 | // Invoke "debug paint" (press "p" in the console where you ran
83 | // "flutter run", or select "Toggle Debug Paint" from the Flutter tool
84 | // window in IntelliJ) to see the wireframe for each widget.
85 | //
86 | // Column has various properties to control how it sizes itself and
87 | // how it positions its children. Here we use mainAxisAlignment to
88 | // center the children vertically; the main axis here is the vertical
89 | // axis because Columns are vertical (the cross axis would be
90 | // horizontal).
91 | mainAxisAlignment: MainAxisAlignment.center,
92 | children: [
93 | new Text(
94 | 'You have pushed the button this many times:',
95 | ),
96 | new Text(
97 | '$_counter',
98 | style: Theme.of(context).textTheme.display1,
99 | ),
100 | new GestureDetector(
101 | onTap: () {
102 | Navigator.push(
103 | context,
104 | new MaterialPageRoute(
105 | builder: (context) => new SamplePage()));
106 | },
107 | child: new Text(
108 | "点击我跳转到Sample页面",
109 | style: new TextStyle(fontSize: 26.0, color: Colors.red),
110 | ),
111 | ),
112 | ],
113 | ),
114 | ),
115 | floatingActionButton: new FloatingActionButton(
116 | onPressed: _incrementCounter,
117 | tooltip: 'Increment',
118 | child: new Icon(Icons.add),
119 | ), // This trailing comma makes auto-formatting nicer for build methods.
120 | );
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_layout_demo
2 | description: A Flutter Layout Demo Application.
3 |
4 | dependencies:
5 | flutter:
6 | sdk: flutter
7 |
8 | # The following adds the Cupertino Icons font to your application.
9 | # Use with the CupertinoIcons class for iOS style icons.
10 | cupertino_icons: ^0.1.0
11 |
12 | dev_dependencies:
13 | flutter_test:
14 | sdk: flutter
15 |
16 |
17 | # For information on the generic Dart part of this file, see the
18 | # following page: https://www.dartlang.org/tools/pub/pubspec
19 |
20 | # The following section is specific to Flutter.
21 | flutter:
22 |
23 | # The following line ensures that the Material Icons font is
24 | # included with your application, so that you can use the icons in
25 | # the material Icons class.
26 | uses-material-design: true
27 |
28 | # To add assets to your application, add an assets section, like this:
29 | assets:
30 | - assets/images/avatar2.png
31 | - assets/images/nav_close.png
32 | - assets/images/publish_chat_box.png
33 | - assets/images/publish_work_line.png
34 | - assets/images/publish_work_sign.png
35 | - assets/images/share_qq.png
36 | - assets/images/share_wechat.png
37 |
38 | # An image asset can refer to one or more resolution-specific "variants", see
39 | # https://flutter.io/assets-and-images/#resolution-aware.
40 |
41 | # For details regarding adding assets from package dependencies, see
42 | # https://flutter.io/assets-and-images/#from-packages
43 |
44 | # To add custom fonts to your application, add a fonts section here,
45 | # in this "flutter" section. Each entry in this list should have a
46 | # "family" key with the font family name, and a "fonts" key with a
47 | # list giving the asset and other descriptors for the font. For
48 | # example:
49 | # fonts:
50 | # - family: Schyler
51 | # fonts:
52 | # - asset: fonts/Schyler-Regular.ttf
53 | # - asset: fonts/Schyler-Italic.ttf
54 | # style: italic
55 | # - family: Trajan Pro
56 | # fonts:
57 | # - asset: fonts/TrajanPro.ttf
58 | # - asset: fonts/TrajanPro_Bold.ttf
59 | # weight: 700
60 | #
61 | # For details regarding fonts from package dependencies,
62 | # see https://flutter.io/custom-fonts/#from-packages
63 |
--------------------------------------------------------------------------------
/demo/3. Flutter布局/flutter_layout_demo/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties
5 | // are correct.
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 |
10 | import 'package:flutter_layout_demo/main.dart';
11 |
12 | void main() {
13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
14 | // Build our app and trigger a frame.
15 | await tester.pumpWidget(new MyApp());
16 |
17 | // Verify that our counter starts at 0.
18 | expect(find.text('0'), findsOneWidget);
19 | expect(find.text('1'), findsNothing);
20 |
21 | // Tap the '+' icon and trigger a frame.
22 | await tester.tap(find.byIcon(Icons.add));
23 | await tester.pump();
24 |
25 | // Verify that our counter has incremented.
26 | expect(find.text('0'), findsNothing);
27 | expect(find.text('1'), findsOneWidget);
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | .flutter-plugins
10 |
11 | # Miscellaneous
12 | *.class
13 | *.lock
14 | *.log
15 | *.pyc
16 | *.swp
17 | .DS_Store
18 | .atom/
19 | .buildlog/
20 | .history
21 | .svn/
22 |
23 | # IntelliJ related
24 | *.iml
25 | *.ipr
26 | *.iws
27 | .idea/
28 |
29 | # Visual Studio Code related
30 | .vscode/
31 |
32 | # Flutter repo-specific
33 | /bin/cache/
34 | /bin/mingit/
35 | /dev/benchmarks/mega_gallery/
36 | /dev/bots/.recipe_deps
37 | /dev/bots/android_tools/
38 | /dev/docs/doc/
39 | /dev/docs/lib/
40 | /dev/docs/pubspec.yaml
41 | /packages/flutter/coverage/
42 | version
43 |
44 | # Flutter/Dart/Pub related
45 | **/doc/api/
46 | .dart_tool/
47 | .flutter-plugins
48 | .packages
49 | .pub-cache/
50 | .pub/
51 | build/
52 | flutter_*.png
53 | linked_*.ds
54 | unlinked.ds
55 | unlinked_spec.ds
56 |
57 | # Android related
58 | **/android/**/gradle-wrapper.jar
59 | **/android/.gradle
60 | **/android/captures/
61 | **/android/gradlew
62 | **/android/gradlew.bat
63 | **/android/local.properties
64 | **/android/**/GeneratedPluginRegistrant.java
65 |
66 | # iOS/XCode related
67 | **/ios/**/*.mode1v3
68 | **/ios/**/*.mode2v3
69 | **/ios/**/*.moved-aside
70 | **/ios/**/*.pbxuser
71 | **/ios/**/*.perspectivev3
72 | **/ios/**/*sync/
73 | **/ios/**/.sconsign.dblite
74 | **/ios/**/.tags*
75 | **/ios/**/.vagrant/
76 | **/ios/**/DerivedData/
77 | **/ios/**/Icon?
78 | **/ios/**/Pods/
79 | **/ios/**/.symlinks/
80 | **/ios/**/profile
81 | **/ios/**/xcuserdata
82 | **/ios/.generated/
83 | **/ios/Flutter/App.framework
84 | **/ios/Flutter/Flutter.framework
85 | **/ios/Flutter/Generated.xcconfig
86 | **/ios/Flutter/app.flx
87 | **/ios/Flutter/app.zip
88 | **/ios/Flutter/flutter_assets/
89 | **/ios/ServiceDefinitions.json
90 | **/ios/Runner/GeneratedPluginRegistrant.*
91 |
92 | # Exceptions to above rules.
93 | !**/ios/**/default.mode1v3
94 | !**/ios/**/default.mode2v3
95 | !**/ios/**/default.pbxuser
96 | !**/ios/**/default.perspectivev3
97 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: f9bb4289e9fd861d70ae78bcc3a042ef1b35cc9d
8 | channel: beta
9 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/README.md:
--------------------------------------------------------------------------------
1 | # flutter_layout_demo
2 |
3 | A Flutter Layout Demo application.
4 |
5 | ## Getting Started
6 |
7 | For help getting started with Flutter, view our online
8 | [documentation](https://flutter.io/).
9 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.class
3 | .gradle
4 | /local.properties
5 | /.idea/workspace.xml
6 | /.idea/libraries
7 | .DS_Store
8 | /build
9 | /captures
10 | GeneratedPluginRegistrant.java
11 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | apply plugin: 'com.android.application'
15 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
16 |
17 | android {
18 | compileSdkVersion 27
19 |
20 | lintOptions {
21 | disable 'InvalidPackage'
22 | }
23 |
24 | defaultConfig {
25 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
26 | applicationId "com.study.flutterlayoutdemo"
27 | minSdkVersion 16
28 | targetSdkVersion 27
29 | versionCode 1
30 | versionName "1.0"
31 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
32 | }
33 |
34 | buildTypes {
35 | release {
36 | // TODO: Add your own signing config for the release build.
37 | // Signing with the debug keys for now, so `flutter run --release` works.
38 | signingConfig signingConfigs.debug
39 | }
40 | }
41 | }
42 |
43 | flutter {
44 | source '../..'
45 | }
46 |
47 | dependencies {
48 | testImplementation 'junit:junit:4.12'
49 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
50 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
51 | }
52 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
15 |
19 |
26 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/java/com/study/flutterlayoutdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.study.flutterlayoutdemo;
2 |
3 | import android.os.Bundle;
4 |
5 | import io.flutter.app.FlutterActivity;
6 | import io.flutter.plugins.GeneratedPluginRegistrant;
7 |
8 | public class MainActivity extends FlutterActivity {
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | GeneratedPluginRegistrant.registerWith(this);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.0.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/images/pic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/images/pic.jpg
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/app.flx
37 | /Flutter/app.zip
38 | /Flutter/flutter_assets/
39 | /Flutter/App.framework
40 | /Flutter/Flutter.framework
41 | /Flutter/Generated.xcconfig
42 | /ServiceDefinitions.json
43 |
44 | Pods/
45 | .symlinks/
46 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
7 | [GeneratedPluginRegistrant registerWithRegistry:self];
8 | // Override point for customization after application launch.
9 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
10 | }
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yang7229693/flutter-study/8e959f3c0ec21360ad8369d9ae1bd381e70ba521/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_layout_demo
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char * argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/helper/ListViewBuilder.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYListViewBuilder extends StatelessWidget {
5 | const LYListViewBuilder();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("ListViewBuilder"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYListViewBuilderContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYListViewBuilderContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return new Center(
23 | child: new Text("ListViewBuilder"),
24 | );
25 | }
26 | }
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_layout_demo/multi/Column.dart';
3 | import 'package:flutter_layout_demo/multi/CustomMultiChildLayout.dart';
4 | import 'package:flutter_layout_demo/multi/Flow.dart';
5 | import 'package:flutter_layout_demo/multi/GridView.dart';
6 | import 'package:flutter_layout_demo/multi/IndexedStack.dart';
7 | import 'package:flutter_layout_demo/multi/ListBody.dart';
8 | import 'package:flutter_layout_demo/multi/ListView.dart';
9 | import 'package:flutter_layout_demo/multi/Row.dart';
10 | import 'package:flutter_layout_demo/multi/Stack.dart';
11 | import 'package:flutter_layout_demo/multi/Table.dart';
12 | import 'package:flutter_layout_demo/multi/Wrap.dart';
13 | import 'package:flutter_layout_demo/single/AspectRatio.dart';
14 | import 'package:flutter_layout_demo/single/Baseline.dart';
15 | import 'package:flutter_layout_demo/single/Center.dart';
16 | import 'package:flutter_layout_demo/single/ConstrainedBox.dart';
17 | import 'package:flutter_layout_demo/single/Container.dart';
18 | import 'package:flutter_layout_demo/single/CustomSingleChildLayout.dart';
19 | import 'package:flutter_layout_demo/single/FittedBox.dart';
20 | import 'package:flutter_layout_demo/single/FractionallySizedBox.dart';
21 | import 'package:flutter_layout_demo/single/IntrinsicHeight.dart';
22 | import 'package:flutter_layout_demo/single/IntrinsicWidth.dart';
23 | import 'package:flutter_layout_demo/single/LimitedBox.dart';
24 | import 'package:flutter_layout_demo/single/Offstage.dart';
25 | import 'package:flutter_layout_demo/single/OverflowBox.dart';
26 | import 'package:flutter_layout_demo/single/Padding.dart';
27 | import 'package:flutter_layout_demo/single/Align.dart';
28 | import 'package:flutter_layout_demo/single/SizedBox.dart';
29 | import 'package:flutter_layout_demo/single/SizedOverflowBox.dart';
30 | import 'package:flutter_layout_demo/single/Transform.dart';
31 |
32 | void main() => runApp(new MyApp());
33 |
34 | const List singleLayoutWidgets = [
35 | "Container",
36 | "Padding",
37 | "Center",
38 | "Align",
39 | "FittedBox",
40 | "AspectRatio",
41 | "ConstrainedBox",
42 | "Baseline",
43 | "FractionallySizedBox",
44 | "IntrinsicHeight",
45 | "IntrinsicWidth",
46 | "LimitedBox",
47 | "Offstage",
48 | "OverflowBox",
49 | "SizedBox",
50 | "SizedOverflowBox",
51 | "Transform",
52 | "CustomSingleChildLayout",
53 | ];
54 |
55 | const List multiLayoutWidgets = [
56 | "Row",
57 | "Column",
58 | "Stack",
59 | "IndexedStack",
60 | "GridView",
61 | "Flow",
62 | "Table",
63 | "Wrap",
64 | "ListBody",
65 | "ListView",
66 | "CustomMultiChildLayout",
67 | ];
68 |
69 | class MyApp extends StatelessWidget {
70 | @override
71 | Widget build(BuildContext context) {
72 | return new MaterialApp(
73 | title: 'Flutter Layout Demo',
74 | theme: new ThemeData(
75 | primarySwatch: Colors.blue,
76 | ),
77 | home: new MyHomePage(title: 'Flutter Demo Home Page'),
78 | routes: {
79 | '/Container': (_) => new LYContainer(),
80 | '/Padding': (_) => new LYPadding(),
81 | '/Center': (_) => new LYCenter(),
82 | '/Align': (_) => new LYAlign(),
83 | '/FittedBox': (_) => new LYFittedBox(),
84 | '/AspectRatio': (_) => new LYAspectRatio(),
85 | '/ConstrainedBox': (_) => new LYConstrainedBox(),
86 | '/Baseline': (_) => new LYBaseline(),
87 | '/FractionallySizedBox': (_) => new LYFractionallySizedBox(),
88 | '/IntrinsicHeight': (_) => new LYIntrinsicHeight(),
89 | '/IntrinsicWidth': (_) => new LYIntrinsicWidth(),
90 | '/LimitedBox': (_) => new LYLimitedBox(),
91 | '/Offstage': (_) => new LYOffstage(),
92 | '/OverflowBox': (_) => new LYOverflowBox(),
93 | '/SizedBox': (_) => new LYSizedBox(),
94 | '/SizedOverflowBox': (_) => new LYSizedOverflowBox(),
95 | '/Transform': (_) => new LYTransform(),
96 | '/CustomSingleChildLayout': (_) => new LYCustomSingleChildLayout(),
97 |
98 | '/Row': (_) => new LYRow(),
99 | '/Column': (_) => new LYColumn(),
100 | '/Stack': (_) => new LYStack(),
101 | '/IndexedStack': (_) => new LYIndexedStack(),
102 | '/GridView': (_) => new LYGridView(),
103 | '/Flow': (_) => new LYFlow(),
104 | '/Table': (_) => new LYTable(),
105 | '/Wrap': (_) => new LYWrap(),
106 | '/ListBody': (_) => new LYListBody(),
107 | '/ListView': (_) => new LYListView(),
108 | '/CustomMultiChildLayout': (_) => new LYCustomMultiChildLayout(),
109 | },
110 | );
111 | }
112 | }
113 |
114 | class MyHomePage extends StatefulWidget {
115 | MyHomePage({Key key, this.title}) : super(key: key);
116 |
117 | final String title;
118 |
119 | @override
120 | _MyHomePageState createState() => new _MyHomePageState();
121 | }
122 |
123 | void _navigateToPage(BuildContext context, String page) {
124 | print("Page:$page");
125 | Navigator.of(context).pushNamed('/$page');
126 | }
127 |
128 | class _MyHomePageState extends State {
129 | @override
130 | Widget build(BuildContext context) {
131 | List layoutWidgets = [];
132 | layoutWidgets.add("--Single-child layout widgets--");
133 | layoutWidgets.addAll(singleLayoutWidgets);
134 | layoutWidgets.add("--Multi-child layout widgets--");
135 | layoutWidgets.addAll(multiLayoutWidgets);
136 |
137 | return new Scaffold(
138 | appBar: new AppBar(
139 | title: new Text(widget.title),
140 | ),
141 | body: new ListView.builder(
142 | itemBuilder: (BuildContext buildContext, int index) {
143 | return new ListTile(
144 | title: new LayoutListItem(
145 | index: index,
146 | title: layoutWidgets[index],
147 | onPress: () {
148 | String item = layoutWidgets[index];
149 | if (item.startsWith("--")) {
150 | return;
151 | }
152 |
153 | _navigateToPage(context, layoutWidgets[index]);
154 | },
155 | ),
156 | );
157 | },
158 | itemCount: (singleLayoutWidgets.length + multiLayoutWidgets.length + 2),
159 | ),
160 | );
161 | }
162 | }
163 |
164 | class LayoutListItem extends StatelessWidget {
165 | LayoutListItem({this.index, this.title, this.onPress});
166 |
167 | final int index;
168 | final String title;
169 | final VoidCallback onPress;
170 |
171 | @override
172 | Widget build(BuildContext context) {
173 | return new GestureDetector(
174 | onTap: onPress,
175 | child: new Center(
176 | child: new Text(
177 | "$title",
178 | style: new TextStyle(fontSize: 16.0, color: Colors.black),
179 | ),
180 | ),
181 | );
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/Column.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYColumn extends StatelessWidget {
5 | const LYColumn();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("Column"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYColumnContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYColumnContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return new Column(
23 | children: [
24 | Expanded(
25 | child: Container(
26 | color: Colors.red,
27 | padding: EdgeInsets.all(5.0),
28 | ),
29 | flex: 1,
30 | ),
31 | Expanded(
32 | child: Container(
33 | color: Colors.yellow,
34 | padding: EdgeInsets.all(5.0),
35 | ),
36 | flex: 2,
37 | ),
38 | Expanded(
39 | child: Container(
40 | color: Colors.blue,
41 | padding: EdgeInsets.all(5.0),
42 | ),
43 | flex: 1,
44 | ),
45 | ],
46 | );
47 | }
48 | }
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/CustomMultiChildLayout.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | const double unitSize = kToolbarHeight;
5 |
6 | class LYCustomMultiChildLayout extends StatelessWidget {
7 | const LYCustomMultiChildLayout();
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return new Scaffold(
12 | appBar: new AppBar(
13 | title: new Text("CustomMultiChildLayout"),
14 | leading: new BackButton(),
15 | ),
16 | body: new LYCustomMultiChildLayoutContent(),
17 | );
18 | }
19 | }
20 |
21 | class LYCustomMultiChildLayoutContent extends StatelessWidget {
22 | @override
23 | Widget build(BuildContext context) {
24 | return Container(
25 | width: 200.0,
26 | height: 100.0,
27 | color: Colors.yellow,
28 | child: CustomMultiChildLayout(
29 | delegate: TestLayoutDelegate(),
30 | children: [
31 | LayoutId(
32 | id: TestLayoutDelegate.title,
33 | child: new Text("This is title",
34 | style: TextStyle(fontSize: 20.0, color: Colors.black)),
35 | ),
36 | LayoutId(
37 | id: TestLayoutDelegate.description,
38 | child: new Text("This is description",
39 | style: TextStyle(fontSize: 14.0, color: Colors.red)),
40 | ),
41 | ],
42 | ),
43 | );
44 | }
45 | }
46 |
47 | class TestLayoutDelegate extends MultiChildLayoutDelegate {
48 | TestLayoutDelegate();
49 |
50 | static const String title = 'title';
51 | static const String description = 'description';
52 |
53 | @override
54 | void performLayout(Size size) {
55 | final BoxConstraints constraints =
56 | new BoxConstraints(maxWidth: size.width);
57 |
58 | final Size titleSize = layoutChild(title, constraints);
59 | positionChild(title, new Offset(0.0, 0.0));
60 |
61 | final double descriptionY = titleSize.height;
62 | layoutChild(description, constraints);
63 | positionChild(description, new Offset(0.0, descriptionY));
64 | }
65 |
66 | @override
67 | bool shouldRelayout(TestLayoutDelegate oldDelegate) => false;
68 | }
69 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/Flow.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYFlow extends StatelessWidget {
5 | const LYFlow();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("Flow"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYFlowContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYFlowContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | const width = 80.0;
23 | const height = 60.0;
24 | return Flow(
25 | delegate: TestFlowDelegate(margin: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0)),
26 | children: [
27 | new Container(width: width, height: height, color: Colors.yellow,),
28 | new Container(width: width, height: height, color: Colors.green,),
29 | new Container(width: width, height: height, color: Colors.red,),
30 | new Container(width: width, height: height, color: Colors.black,),
31 | new Container(width: width, height: height, color: Colors.blue,),
32 | new Container(width: width, height: height, color: Colors.lightGreenAccent,),
33 | ],
34 | );
35 | }
36 | }
37 |
38 | class TestFlowDelegate extends FlowDelegate {
39 | EdgeInsets margin = EdgeInsets.zero;
40 | TestFlowDelegate({this.margin});
41 |
42 | @override
43 | void paintChildren(FlowPaintingContext context) {
44 | var x = margin.left;
45 | var y = margin.top;
46 | for (int i = 0; i < context.childCount; i++) {
47 | var w = context.getChildSize(i).width + x + margin.right;
48 | if (w < context.size.width) {
49 | context.paintChild(i,
50 | transform: new Matrix4.translationValues(
51 | x, y, 0.0));
52 | x = w + margin.left;
53 | } else {
54 | x = margin.left;
55 | y += context.getChildSize(i).height + margin.top + margin.bottom;
56 | context.paintChild(i,
57 | transform: new Matrix4.translationValues(
58 | x, y, 0.0));
59 | x += context.getChildSize(i).width + margin.left + margin.right;
60 | }
61 | }
62 | }
63 |
64 | @override
65 | bool shouldRepaint(FlowDelegate oldDelegate) {
66 | return oldDelegate != this;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/GridView.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYGridView extends StatelessWidget {
5 | const LYGridView();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("GridView"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYGridViewContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYGridViewContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Container(
23 | color: Colors.yellow,
24 | width: 200.0,
25 | height: 300.0,
26 | child: GridView.count(
27 | crossAxisCount: 2,
28 | children: List.generate(
29 | 100,
30 | (index) {
31 | return Center(
32 | child: Text(
33 | 'Item $index',
34 | style: Theme.of(context).textTheme.headline,
35 | ),
36 | );
37 | },
38 | ),
39 | ),
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/IndexedStack.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYIndexedStack extends StatelessWidget {
5 | const LYIndexedStack();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("IndexedStack"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYIndexedStackContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYIndexedStackContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Container(
23 | color: Colors.yellow,
24 | child: IndexedStack(
25 | index: 1,
26 | alignment: const Alignment(0.6, 0.6),
27 | children: [
28 | CircleAvatar(
29 | backgroundImage: AssetImage('images/pic.jpg'),
30 | radius: 100.0,
31 | ),
32 | Container(
33 | decoration: BoxDecoration(
34 | color: Colors.black45,
35 | ),
36 | child: Text(
37 | 'Mia B',
38 | style: TextStyle(
39 | fontSize: 20.0,
40 | fontWeight: FontWeight.bold,
41 | color: Colors.white,
42 | ),
43 | ),
44 | ),
45 | ],
46 | ),
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/ListBody.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYListBody extends StatelessWidget {
5 | const LYListBody();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("ListBody"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYListBodyContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYListBodyContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Flex(
23 | direction: Axis.vertical,
24 | children: [
25 | ListBody(
26 | mainAxis: Axis.vertical,
27 | reverse: false,
28 | children: [
29 | Container(color: Colors.red, width: 50.0, height: 50.0,),
30 | Container(color: Colors.yellow, width: 50.0, height: 50.0,),
31 | Container(color: Colors.green, width: 50.0, height: 50.0,),
32 | Container(color: Colors.blue, width: 50.0, height: 50.0,),
33 | Container(color: Colors.black, width: 50.0, height: 50.0,),
34 | ],
35 | )],
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/ListView.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYListView extends StatelessWidget {
5 | const LYListView();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("ListView"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYListViewContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYListViewContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return ListView(
23 | padding: EdgeInsets.all(20.0),
24 | children: [
25 | Text('I\'m dedicating every day to you'),
26 | Text('Domestic life was never quite my style'),
27 | Text('When you smile, you knock me out, I fall apart'),
28 | Text('And I thought I was so smart'),
29 | ],
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/Row.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYRow extends StatelessWidget {
5 | const LYRow();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("Row"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYRowContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYRowContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Row(
23 | children: [
24 | Expanded(
25 | child: Container(
26 | color: Colors.red,
27 | padding: EdgeInsets.all(5.0),
28 | ),
29 | flex: 1,
30 | ),
31 | Expanded(
32 | child: Container(
33 | color: Colors.yellow,
34 | padding: EdgeInsets.all(5.0),
35 | ),
36 | flex: 2,
37 | ),
38 | Expanded(
39 | child: Container(
40 | color: Colors.blue,
41 | padding: EdgeInsets.all(5.0),
42 | ),
43 | flex: 1,
44 | ),
45 | ],
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/Stack.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYStack extends StatelessWidget {
5 | const LYStack();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("Stack"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYStackContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYStackContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Stack(
23 | alignment: const Alignment(0.6, 0.6),
24 | children: [
25 | CircleAvatar(
26 | backgroundImage: AssetImage('images/pic.jpg'),
27 | radius: 100.0,
28 | ),
29 | Container(
30 | decoration: BoxDecoration(
31 | color: Colors.black45,
32 | ),
33 | child: Text(
34 | 'Mia B',
35 | style: TextStyle(
36 | fontSize: 20.0,
37 | fontWeight: FontWeight.bold,
38 | color: Colors.white,
39 | ),
40 | ),
41 | ),
42 | ],
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/Table.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYTable extends StatelessWidget {
5 | const LYTable();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("Table"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYTableContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYTableContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Table(
23 | columnWidths: const {
24 | 0: FixedColumnWidth(50.0),
25 | 1: FixedColumnWidth(100.0),
26 | 2: FixedColumnWidth(50.0),
27 | 3: FixedColumnWidth(100.0),
28 | },
29 | border: TableBorder.all(color: Colors.red, width: 1.0, style: BorderStyle.solid),
30 | children: const [
31 | TableRow(
32 | children: [
33 | Text('A1'),
34 | Text('B1'),
35 | Text('C1'),
36 | Text('D1'),
37 | ],
38 | ),
39 | TableRow(
40 | children: [
41 | Text('A2'),
42 | Text('B2'),
43 | Text('C2'),
44 | Text('D2'),
45 | ],
46 | ),
47 | TableRow(
48 | children: [
49 | Text('A3'),
50 | Text('B3'),
51 | Text('C3'),
52 | Text('D3'),
53 | ],
54 | ),
55 | ],
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/multi/Wrap.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | class LYWrap extends StatelessWidget {
5 | const LYWrap();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("Wrap"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYWrapContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYWrapContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Wrap(
23 | spacing: 8.0, // gap between adjacent chips
24 | runSpacing: 4.0, //
25 | children: [
26 | Chip(
27 | avatar: CircleAvatar(
28 | backgroundColor: Colors.blue.shade900, child: new Text('AH', style: TextStyle(fontSize: 10.0),)),
29 | label: Text('Hamilton'),
30 | ),
31 | Chip(
32 | avatar: CircleAvatar(
33 | backgroundColor: Colors.blue.shade900, child: new Text('ML', style: TextStyle(fontSize: 10.0),)),
34 | label: Text('Lafayette'),
35 | ),
36 | Chip(
37 | avatar: CircleAvatar(
38 | backgroundColor: Colors.blue.shade900, child: new Text('HM', style: TextStyle(fontSize: 10.0),)),
39 | label: Text('Mulligan'),
40 | ),
41 | Chip(
42 | avatar: CircleAvatar(
43 | backgroundColor: Colors.blue.shade900, child: new Text('JL', style: TextStyle(fontSize: 10.0),)),
44 | label: Text('Laurens'),
45 | ),
46 | ],
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/Align.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYAlign extends StatelessWidget {
4 | const LYAlign();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("Align"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYAlignContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYAlignContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new Container(
22 | color: Colors.red,
23 | child: new Align(
24 | alignment: const Alignment(1.0, 0.5),
25 | widthFactor: 3.0,
26 | heightFactor: 3.0,
27 | child: new Container(
28 | child: new Text("Align"),
29 | color: Colors.amber,
30 | ),
31 | ),
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/AspectRatio.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYAspectRatio extends StatelessWidget {
4 | const LYAspectRatio();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("AspectRatio"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYAspectRatioContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYAspectRatioContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new Container(
22 | height: 200.0,
23 | child: new AspectRatio(
24 | aspectRatio: 1.5,
25 | child: new Container(
26 | color: Colors.red,
27 | ),
28 | ),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/Baseline.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYBaseline extends StatelessWidget {
4 | const LYBaseline();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("Baseline"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYBaselineContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYBaselineContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new Row(
22 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
23 | children: [
24 | new Baseline(
25 | baseline: 50.0,
26 | baselineType: TextBaseline.alphabetic,
27 | child: new Text(
28 | 'TjTjTj',
29 | style: new TextStyle(
30 | fontSize: 16.0,
31 | textBaseline: TextBaseline.alphabetic,
32 | ),
33 | ),
34 | ),
35 | new Baseline(
36 | baseline: 50.0,
37 | baselineType: TextBaseline.alphabetic,
38 | child: new Container(
39 | width: 30.0,
40 | height: 30.0,
41 | color: Colors.red,
42 | ),
43 | ),
44 | new Baseline(
45 | baseline: 50.0,
46 | baselineType: TextBaseline.alphabetic,
47 | child: new Text(
48 | 'RyRyRy',
49 | style: new TextStyle(
50 | fontSize: 35.0,
51 | ),
52 | ),
53 | ),
54 | ],
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/Center.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYCenter extends StatelessWidget {
4 | const LYCenter();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("Center"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYCenterContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYCenterContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new Center(
22 | child: new Text("Center"),
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/ConstrainedBox.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYConstrainedBox extends StatelessWidget {
4 | const LYConstrainedBox();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("ConstrainedBox"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYConstrainedBoxContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYConstrainedBoxContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new ConstrainedBox(
22 | constraints: const BoxConstraints(
23 | minWidth: 100.0,
24 | minHeight: 100.0,
25 | maxWidth: 150.0,
26 | maxHeight: 150.0,
27 | ),
28 | child: new Container(
29 | width: 200.0,
30 | height: 200.0,
31 | color: Colors.red,
32 | ),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/Container.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYContainer extends StatelessWidget {
4 | const LYContainer();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | backgroundColor: Colors.white,
10 | appBar: new AppBar(
11 | title: new Text("Container"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYContainerContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYContainerContent extends StatelessWidget {
20 | @override
21 | Widget build(BuildContext context) {
22 | return Container(
23 | margin: const EdgeInsets.all(10.0),
24 | child: new Wrap(
25 | children: [
26 | new Container(
27 | constraints: new BoxConstraints.expand(
28 | height:
29 | Theme.of(context).textTheme.display1.fontSize * 1.1 + 200.0,
30 | ),
31 | decoration: new BoxDecoration(
32 | border: new Border.all(width: 2.0, color: Colors.red),
33 | color: Colors.grey,
34 | borderRadius: new BorderRadius.all(new Radius.circular(20.0)),
35 | image: new DecorationImage(
36 | image: new NetworkImage(
37 | 'http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f06c.jpg'),
38 | centerSlice: new Rect.fromLTRB(270.0, 180.0, 1360.0, 730.0),
39 | ),
40 | ),
41 | padding: const EdgeInsets.all(8.0),
42 | alignment: Alignment.center,
43 | child: new Text('Hello World',
44 | style: Theme
45 | .of(context)
46 | .textTheme
47 | .display1
48 | .copyWith(color: Colors.black)),
49 | transform: new Matrix4.rotationZ(0.3),
50 | ),
51 | new Container(
52 | margin: const EdgeInsets.only(top: 105.0),
53 | child: new LYRoundButton(
54 | title: new Text(
55 | "I am a default button",
56 | style: new TextStyle(
57 | fontSize: 18.0,
58 | color: Colors.white,
59 | ),
60 | ),
61 | disabled: false,
62 | onPress: () {
63 | final snackBar = new SnackBar(content: new Text('Click One!!'));
64 | Scaffold.of(context).showSnackBar(snackBar);
65 | },
66 | ),
67 | ),
68 | new Container(
69 | margin: const EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
70 | child: new LYRoundButton(
71 | width: 250.0,
72 | height: 80.0,
73 | backgroundColor: const Color(0xFF41CB39),
74 | activeBackgroundColor: const Color(0xB341CB39),
75 | disabledBackgroundColor: const Color(0x3341CB39),
76 | title: new Text(
77 | "I am a custom button",
78 | style: new TextStyle(
79 | fontSize: 18.0,
80 | color: Colors.white,
81 | ),
82 | ),
83 | disabled: false,
84 | onPress: () {
85 | final snackBar = new SnackBar(content: new Text('Click Two!!'));
86 | Scaffold.of(context).showSnackBar(snackBar);
87 | },
88 | ),
89 | ),
90 | new LYRoundButton(
91 | title: new Text(
92 | "I`m a disabled button",
93 | style: new TextStyle(
94 | fontSize: 18.0,
95 | color: Colors.white,
96 | ),
97 | ),
98 | disabled: true,
99 | onPress: () {
100 | final snackBar = new SnackBar(content: new Text('Click Three!!'));
101 | Scaffold.of(context).showSnackBar(snackBar);
102 | },
103 | ),
104 | ],
105 | ),
106 | );
107 | }
108 | }
109 |
110 | class LYRoundButton extends StatefulWidget {
111 | static const defaultBackgroundColor = const Color(0xFF8B5FFE);
112 | static const defaultActiveBackgroundColor = const Color(0xB38B5FFE);
113 | static const defaultDisabledBackgroundColor = const Color(0x338B5FFE);
114 |
115 | LYRoundButton({
116 | this.title,
117 | this.onPress,
118 | this.height = 52.0,
119 | this.width = double.infinity,
120 | this.disabled = false,
121 | this.backgroundColor = defaultBackgroundColor,
122 | this.activeBackgroundColor = defaultActiveBackgroundColor,
123 | this.disabledBackgroundColor = defaultDisabledBackgroundColor,
124 | });
125 |
126 | final Widget title;
127 | final Color backgroundColor, activeBackgroundColor, disabledBackgroundColor;
128 | final VoidCallback onPress;
129 | final double height, width;
130 | final bool disabled;
131 |
132 | @override
133 | _LYRoundButtonState createState() => new _LYRoundButtonState();
134 | }
135 |
136 | class _LYRoundButtonState extends State {
137 | Color currentColor;
138 |
139 | @override
140 | void initState() {
141 | super.initState();
142 | if (widget.disabled) {
143 | currentColor = widget.disabledBackgroundColor;
144 | } else {
145 | currentColor = widget.backgroundColor;
146 | }
147 | }
148 |
149 | @override
150 | void deactivate() {
151 | super.deactivate();
152 | currentColor = widget.backgroundColor;
153 | }
154 |
155 | @override
156 | Widget build(BuildContext context) {
157 | return new GestureDetector(
158 | onTap: () {
159 | if (widget.onPress != null && !widget.disabled) {
160 | widget.onPress();
161 | }
162 | },
163 | onTapDown: (TapDownDetails details) {
164 | if (!widget.disabled) {
165 | setState(() {
166 | currentColor = widget.activeBackgroundColor;
167 | });
168 | }
169 | },
170 | onTapUp: (TapUpDetails details) {
171 | if (!widget.disabled) {
172 | setState(() {
173 | currentColor = widget.backgroundColor;
174 | });
175 | }
176 | },
177 | onTapCancel: () {
178 | if (!widget.disabled) {
179 | setState(() {
180 | currentColor = widget.backgroundColor;
181 | });
182 | }
183 | },
184 | child: new Container(
185 | decoration: new BoxDecoration(
186 | color: currentColor,
187 | borderRadius:
188 | new BorderRadius.all(new Radius.circular(widget.height / 2.0)),
189 | ),
190 | height: widget.height,
191 | width: widget.width,
192 | alignment: Alignment.center,
193 | child: widget.title,
194 | ),
195 | );
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/CustomSingleChildLayout.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYCustomSingleChildLayout extends StatelessWidget {
4 | const LYCustomSingleChildLayout();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("CustomSingleChildLayout"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYCustomSingleChildLayoutContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYCustomSingleChildLayoutContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return Container(
22 | color: Colors.blue,
23 | padding: const EdgeInsets.all(5.0),
24 | child: CustomSingleChildLayout(
25 | delegate: FixedSizeLayoutDelegate(Size(200.0, 200.0)),
26 | child: Container(
27 | color: Colors.red,
28 | width: 100.0,
29 | height: 300.0,
30 | ),
31 | ),
32 | );
33 | }
34 | }
35 |
36 | class FixedSizeLayoutDelegate extends SingleChildLayoutDelegate {
37 | FixedSizeLayoutDelegate(this.size);
38 |
39 | final Size size;
40 |
41 | @override
42 | Size getSize(BoxConstraints constraints) => size;
43 |
44 | @override
45 | BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
46 | return new BoxConstraints.tight(size);
47 | }
48 |
49 | @override
50 | bool shouldRelayout(FixedSizeLayoutDelegate oldDelegate) {
51 | return size != oldDelegate.size;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/FittedBox.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYFittedBox extends StatelessWidget {
4 | const LYFittedBox();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("FittedBox"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYFittedBoxContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYFittedBoxContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new Container(
22 | color: Colors.amberAccent,
23 | alignment: Alignment.center,
24 | width: double.infinity,
25 | height: double.infinity,
26 | child: new FittedBox(
27 | fit: BoxFit.fill,
28 | alignment: Alignment.center,
29 | child: new Container(
30 | color: Colors.red,
31 | child: new Text(
32 | "BoxFit.fill",
33 | style: const TextStyle(fontSize: 20.0),
34 | ),
35 | ),
36 | ),
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/FractionallySizedBox.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYFractionallySizedBox extends StatelessWidget {
4 | const LYFractionallySizedBox();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("FractionallySizedBox"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYFractionallySizedBoxContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYFractionallySizedBoxContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new Center(
22 | child: new Container(
23 | color: Colors.blue,
24 | height: 150.0,
25 | width: 150.0,
26 | padding: const EdgeInsets.all(10.0),
27 | child: new FractionallySizedBox(
28 | alignment: Alignment.topLeft,
29 | widthFactor: 1.5,
30 | heightFactor: 0.5,
31 | child: new Container(
32 | color: Colors.red,
33 | ),
34 | ),
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/IntrinsicHeight.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYIntrinsicHeight extends StatelessWidget {
4 | const LYIntrinsicHeight();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("IntrinsicHeight"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYIntrinsicHeightContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYIntrinsicHeightContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new IntrinsicHeight(
22 | child: new Row(
23 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
24 | children: [
25 | new Container(color: Colors.blue, width: 100.0),
26 | new Container(color: Colors.red, width: 50.0,height: 50.0,),
27 | new Container(color: Colors.yellow, width: 150.0),
28 | ],
29 | ),
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/IntrinsicWidth.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYIntrinsicWidth extends StatelessWidget {
4 | const LYIntrinsicWidth();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("IntrinsicWidth"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYIntrinsicWidthContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYIntrinsicWidthContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new Container(
22 | color: Colors.green,
23 | padding: const EdgeInsets.all(5.0),
24 | child: new IntrinsicWidth(
25 | stepHeight: 450.0,
26 | stepWidth: 300.0,
27 | child: new Column(
28 | children: [
29 | new Container(color: Colors.blue, height: 100.0),
30 | new Container(color: Colors.red, width: 150.0, height: 100.0),
31 | new Container(color: Colors.yellow, height: 150.0,),
32 | ],
33 | ),
34 | ),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/LimitedBox.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYLimitedBox extends StatelessWidget {
4 | const LYLimitedBox();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("LimitedBox"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYLimitedBoxContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYLimitedBoxContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return Row(
22 | children: [
23 | Container(
24 | color: Colors.red,
25 | width: 100.0,
26 | ),
27 | LimitedBox(
28 | // maxWidth: 150.0,
29 | child: Container(
30 | color: Colors.blue,
31 | width: 250.0,
32 | ),
33 | ),
34 | ],
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/Offstage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class LYOffstage extends StatelessWidget {
5 | const LYOffstage();
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return new Scaffold(
10 | appBar: new AppBar(
11 | title: new Text("Offstage"),
12 | leading: new BackButton(),
13 | ),
14 | body: new LYOffstageContent(),
15 | );
16 | }
17 | }
18 |
19 | class LYOffstageContent extends StatefulWidget {
20 | @override
21 | _LYOffstageContentState createState() => _LYOffstageContentState();
22 | }
23 |
24 | class _LYOffstageContentState extends State {
25 | bool offstage;
26 |
27 | @override
28 | void initState() {
29 | super.initState();
30 | offstage = false;
31 | }
32 |
33 | @override
34 | Widget build(BuildContext context) {
35 | return Column(
36 | children: [
37 | new Offstage(
38 | offstage: offstage,
39 | child: Container(color: Colors.blue, height: 100.0),
40 | ),
41 | new CupertinoButton(
42 | child: Text("点击切换显示"),
43 | onPressed: () {
44 | setState(() {
45 | offstage = !offstage;
46 | });
47 | },
48 | ),
49 | ],
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/OverflowBox.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYOverflowBox extends StatelessWidget {
4 | const LYOverflowBox();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("OverflowBox"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYOverflowBoxContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYOverflowBoxContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return Container(
22 | color: Colors.green,
23 | width: 200.0,
24 | height: 200.0,
25 | padding: const EdgeInsets.all(5.0),
26 | child: OverflowBox(
27 | alignment: Alignment.topLeft,
28 | maxWidth: 300.0,
29 | maxHeight: 300.0,
30 | minWidth: 250.0,
31 | minHeight: 250.0,
32 | child: Container(
33 | color: Color(0x33FF00FF),
34 | width: 200.0,
35 | height: 200.0,
36 | ),
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/Padding.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYPadding extends StatelessWidget {
4 | const LYPadding();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("Padding"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYPaddingContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYPaddingContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return new Padding(
22 | padding: const EdgeInsets.fromLTRB(20.0, 50.0, 40.0, 40.0),
23 | child: new Text("Padding"),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/SizedBox.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYSizedBox extends StatelessWidget {
4 | const LYSizedBox();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("SizedBox"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYSizedBoxContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYSizedBoxContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return Container(
22 | color: Colors.green,
23 | padding: const EdgeInsets.all(5.0),
24 | child: SizedBox(
25 | width: 200.0,
26 | height: 200.0,
27 | child: Container(
28 | color: Colors.red,
29 | width: 100.0,
30 | height: 300.0,
31 | ),
32 | ),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/SizedOverflowBox.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYSizedOverflowBox extends StatelessWidget {
4 | const LYSizedOverflowBox();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("SizedOverflowBox"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYSizedOverflowBoxContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYSizedOverflowBoxContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return Container(
22 | color: Colors.green,
23 | alignment: Alignment.topRight,
24 | width: 200.0,
25 | height: 200.0,
26 | padding: EdgeInsets.all(5.0),
27 | child: SizedOverflowBox(
28 | size: Size(100.0, 200.0),
29 | child: Container(color: Colors.red, width: 200.0, height: 100.0,),
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/lib/single/Transform.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LYTransform extends StatelessWidget {
4 | const LYTransform();
5 |
6 | @override
7 | Widget build(BuildContext context) {
8 | return new Scaffold(
9 | appBar: new AppBar(
10 | title: new Text("Transform"),
11 | leading: new BackButton(),
12 | ),
13 | body: new LYTransformContent(),
14 | );
15 | }
16 | }
17 |
18 | class LYTransformContent extends StatelessWidget {
19 | @override
20 | Widget build(BuildContext context) {
21 | return Center(
22 | child: Container(
23 | color: Colors.red,
24 | child: Transform(
25 | origin: Offset.zero,
26 | alignment: Alignment.topLeft,
27 | transform: Matrix4.rotationZ(0.3),
28 | child: Container(
29 | color: Colors.blue,
30 | width: 100.0,
31 | height: 100.0,
32 | ),
33 | ),
34 | ),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_layout_demo
2 | description: A Flutter Layout Demo application.
3 |
4 | dependencies:
5 | flutter:
6 | sdk: flutter
7 |
8 | # The following adds the Cupertino Icons font to your application.
9 | # Use with the CupertinoIcons class for iOS style icons.
10 | cupertino_icons: ^0.1.2
11 |
12 | dev_dependencies:
13 | flutter_test:
14 | sdk: flutter
15 |
16 |
17 | # For information on the generic Dart part of this file, see the
18 | # following page: https://www.dartlang.org/tools/pub/pubspec
19 |
20 | # The following section is specific to Flutter.
21 | flutter:
22 |
23 | # The following line ensures that the Material Icons font is
24 | # included with your application, so that you can use the icons in
25 | # the material Icons class.
26 | uses-material-design: true
27 |
28 | # To add assets to your application, add an assets section, like this:
29 | assets:
30 | - images/pic.jpg
31 | # - images/a_dot_ham.jpeg
32 |
33 | # An image asset can refer to one or more resolution-specific "variants", see
34 | # https://flutter.io/assets-and-images/#resolution-aware.
35 |
36 | # For details regarding adding assets from package dependencies, see
37 | # https://flutter.io/assets-and-images/#from-packages
38 |
39 | # To add custom fonts to your application, add a fonts section here,
40 | # in this "flutter" section. Each entry in this list should have a
41 | # "family" key with the font family name, and a "fonts" key with a
42 | # list giving the asset and other descriptors for the font. For
43 | # example:
44 | # fonts:
45 | # - family: Schyler
46 | # fonts:
47 | # - asset: fonts/Schyler-Regular.ttf
48 | # - asset: fonts/Schyler-Italic.ttf
49 | # style: italic
50 | # - family: Trajan Pro
51 | # fonts:
52 | # - asset: fonts/TrajanPro.ttf
53 | # - asset: fonts/TrajanPro_Bold.ttf
54 | # weight: 700
55 | #
56 | # For details regarding fonts from package dependencies,
57 | # see https://flutter.io/custom-fonts/#from-packages
58 |
--------------------------------------------------------------------------------
/demo/4. Flutter布局Widget介绍/flutter_layout_demo/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties
5 | // are correct.
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 |
10 | import 'package:flutter_layout_demo/main.dart';
11 |
12 | void main() {
13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
14 | // Build our app and trigger a frame.
15 | await tester.pumpWidget(new MyApp());
16 |
17 | // Verify that our counter starts at 0.
18 | expect(find.text('0'), findsOneWidget);
19 | expect(find.text('1'), findsNothing);
20 |
21 | // Tap the '+' icon and trigger a frame.
22 | await tester.tap(find.byIcon(Icons.add));
23 | await tester.pump();
24 |
25 | // Verify that our counter has incremented.
26 | expect(find.text('0'), findsNothing);
27 | expect(find.text('1'), findsOneWidget);
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/post/1. Flutter 不一样的跨平台解决方案.md:
--------------------------------------------------------------------------------
1 | # Flutter 不一样的跨平台解决方案
2 |
3 | > 本文主要介绍Flutter相关的东西,包括Fuchsia、Dart、Flutter特性、安装以及整体架构等内容。
4 |
5 | ## 简介
6 |
7 | Flutter作为谷歌最近推出的跨平台开发框架,一经推出便吸引了不少注意。关于Flutter,目前我们知道它是一个跨平台开发框架。但是它本身并不止于此,例如Fuchsia、Dart等,我们也都需要去了解。
8 |
9 | ### Fuchsia
10 |
11 | 说到Flutter,绝对绕不开Fuchsia,这个是谷歌开发的一款全新的操作系统,[GitHub地址](https://github.com/fuchsia-mirror)以及[Google source主页](https://fuchsia.googlesource.com/)。Fuchsia内核是Magenta Kernel,一个基于[LittleKernel](https://github.com/littlekernel/lk)的项目。该系统与Android相比,无论是存储器还是内存之类的硬件要求都大幅降低,外界推论是一款面向物联网的系统。笔者倒是没有查到谷歌开发这款操作系统的目的,如果有知晓的,也烦请告知。
12 |
13 | 就像很多博客主说的那样,如果只是取代Android,那无疑是一种很不好的做法。任何技术的推动,都得靠背后的商业驱动,尤其是这种涉及到手机厂商利益的技术。
14 |
15 | ### Flutter
16 |
17 | Flutter是Fuchsia的开发框架,是一套移动UI框架,可以快速在iOS、Android以及Fuchsia上构建高质量的原生用户界面。 目前Flutter是完全免费、开源的,[GitHub地址](https://github.com/flutter/flutter)。其官方编程语言为Dart,也是一门全新的语言。所以说,上手成本比较高,对于移动端开发人员,语言以及框架都是全新的,整个技术栈的积累也都得从头开始。
18 |
19 | 可以看下其官方介绍的特性:
20 |
21 | * 快速开发:Flutter的热重载可以快速地进行测试、构建UI、添加功能并更快地修复错误。
22 | * 富有表现力,漂亮的用户界面:自带的Material Design和Cupertino(iOS风格)widget、丰富的motion API、平滑而自然的滑动效果。
23 | * 响应式框架:使用Flutter的现代、响应式框架,和一系列基础widget,轻松构建您的用户界面。
24 | * 访问本地功能和SDK:Flutter可以复用现有的Java、Swift或ObjC代码,访问iOS和Android上的原生系统功能和系统SDK。
25 | * 统一的应用开发体验:Flutter拥有丰富的工具和库,可以帮助开发者轻松地同时在iOS和Android系统中实现想法和创意。
26 | * 原生性能:Flutter包含了许多核心的widget,如滚动、导航、图标和字体等,这些都可以在iOS和Android上达到原生应用一样的性能。
27 |
28 | 其实从官方特性来看,唯一有点吸引力的就是统一的应用开发体验。一套代码运行在多个平台,做到真正的跨平台。像热加载,目前Android开发本身就支持了,响应式框架以及访问本地功能和SDK,对于Native来说,本身并没有多大的吸引。至于漂亮的用户界面,国内的商业项目,哪一个会去按照Material Design去设计。
29 |
30 | 跨平台本身,往往意味着性能受损,通用性解决不了的问题,又得回到Native去实现。所以这些因素也是跨平台从移动端诞生之初就开始提,到现在也没有被很好解决的一个原因。至于谷歌能够做到什么程度,或者说开发者该保持什么期许,我觉得都不好说,或许谷歌解决了一个多年的难题,或者又像被谷歌放弃掉的其他项目一样。抛开商业层面,对于技术人员,我们更多的是应该去关注它的思想,谷歌是如何去解决这些实际存在很久的问题的,至于技术本身的发展,这个是个人开发者无法去左右的,技术的更迭,保持一种学习的状态,然后努力锻炼身体,就能够保证不被淘汰掉。
31 |
32 | ### Dart
33 |
34 | Dart是谷歌开发的计算机编程语言,于2011年10月份发布,可以被用于web、服务器、移动端和物联网等领域的开发。Flutter采用Dart,原因很多,抛开商业层面的Java版权问题,单纯从技术层面:
35 |
36 | * Dart是AOT(Ahead Of Time)编译的,编译成快速、可预测的本地代码,使Flutter几乎都可以使用Dart编写;
37 | * Dart也可以JIT(Just In Time)编译,开发周期快;
38 | * Dart可以更轻松地创建以60fps运行的流畅动画和转场;
39 | * Dart使Flutter不需要单独的声明式布局语言;
40 | * Dart容易学习,具有静态和动态语言用户都熟悉的特性。
41 |
42 | Dart最初设计是为了取代JavaScript成为web开发的首选语言,最后的结果可想而知,到Dart 2的发布,专注于改善构建客户端应用程序的体验,可以看出定位的转变。用过Java、Kotlin的人,可以很快的上手Dart。
43 |
44 | 一门语言的成败,抛开背后的商业推动,我想很重要的一点在于其生态,生态的好坏,主要包括开发者以及第三方库的数目,目前看,Dart的生态还是比较差。对于个人开发者,可以根据心情来选择,但是对于商业应用,有更复杂的考量标准。Dart背后有谷歌的推动,能发展到什么程度,还得看其商业运作能力了。
45 |
46 | ## 配置
47 |
48 | 此部分针对Mac平台,[Windows平台的安装配置](https://flutterchina.club/setup-windows/),[Linux平台的安装配置](https://flutterchina.club/setup-linux/)。由于笔者主要做移动端开发,如果想使用Flutter进行iOS和Android全平台的开发,环境也建议是Mac平台,毕竟iOS只能在Mac下进行模拟调试。
49 |
50 | ### 安装Flutter
51 |
52 | ```
53 | git clone -b beta https://github.com/flutter/flutter.git
54 | export PUB_HOSTED_URL=https://pub.flutter-io.cn //国内用户需要设置
55 | export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //国内用户需要设置
56 | export PATH=`pwd`/flutter/bin:$PATH
57 | ```
58 |
59 | ### iOS设置
60 |
61 | ```
62 | brew update
63 | brew install --HEAD libimobiledevice
64 | brew install ideviceinstaller ios-deploy cocoapods
65 | pod setup
66 | ```
67 |
68 | ### Android设置
69 |
70 | 下载Android Studio,安装Flutter插件,会将Dart插件也一起安装。
71 |
72 | ### 体验Flutter
73 |
74 | IDE建议选择Android Studio,安装了Flutter插件后,Flutter的开发跟Android
75 | 开发类似,附带三种模版工程、断点调试等。
76 |
77 | 在Android Studio里面新建一个Flutter Application的项目,选择模拟器或者直接连接真机运行,就可以看到一个简单的Flutter应用了,可以在Android和iOS不同平台下看看差异。
78 |
79 | ## Flutter架构
80 |
81 | Flutter是一款移动应用程序SDK,一份代码可以同时生成iOS和Android两个高性能、高保真的应用程序。
82 |
83 | 
84 |
85 | Flutter对于移动开发人员,最诱惑的能力是其完全的跨平台特性,不同于RN这种一处学到处写,它是一处写到出跑,但是他跟其他的跨平台有何区别呢?
86 |
87 | ### 跨平台解决方案
88 |
89 | 市面上的跨平台解决方案,可以大致归结为两类:
90 |
91 | * 使用平台支持的web技术:这些解决方案基本上加载了应用程序中的移动浏览器,并在该浏览器中执行所有的逻辑,例如PhoneGap。
92 | * 本地跨平台:程序员编写的代码自动转换为Native代码,这种方式的优点是近乎原生的性能,例如RN、Weex、Xamarin等。
93 |
94 | 这些方案是否真正的解决了跨平台问题呢?从目前的状况来看,很显然是没有的,因为它们都始终逃不开性能、包大小、流畅性、内存、平台特性等问题。
95 |
96 | 
97 |
98 | RN单独拧出来说,是因为它们并不是追求的一次写到处跑,FB自己也知道不现实,所以把口号改成一次学到处写,去考虑平台的特性,去考虑这个被跨平台方案经常忽略的问题。但是RN也并没有被广泛的接纳,从阿里开始使用到放弃,里面的很多坑都绕不过去。写一次到处跑确实很诱人,从企业角度讲,可以节省大量的人力,但是却忽略了一个很基础的问题,不同平台是否希望如此,苹果是否会愿意自己的生态被打破,不同平台的特性是否应该被归为一致。
99 |
100 | ### Flutter的跨平台解决方案
101 |
102 | 上面简单说了传统跨平台解决方案,我们再回过头看看Flutter的解决方案,Flutter跨平台最核心的部分,是它的高性能渲染引擎(Flutter Engine)。Flutter不使用浏览器技术,也不使用Native的原生控件,它使用自己的渲染引擎来绘制widget。
103 |
104 | 说到widget,就要说一句Flutter的`一切皆为widget`理念。widget是Flutter应用程序用户界面的基本构建块。每个widget都是用户界面一部分的不可变声明。与其他将视图、控制器、布局和其他属性分离的框架不同,Flutter具有一致的统一对象模型:widget。在更新widget的时候,框架能够更加的高效。
105 |
106 | 对于Android平台,Flutter引擎的C/C++代码是由NDK编译,在iOS平台,则是由LLVM编译,两个平台的Dart代码都是AOT编译为本地代码,Flutter应用程序使用本机指令集运行。
107 |
108 | Flutter正是是通过使用相同的渲染器、框架和一组widget,来同时构建iOS和Android应用,而无需维护两套独立的代码库。
109 |
110 | 
111 |
112 | Flutter将UI组件和渲染器从平台移动到应用程序中,这使得它们可以自定义和可扩展。Flutter唯一要求系统提供的是canvas,以便定制的UI组件可以出现在设备的屏幕上。
113 |
114 | ### Flutter框架
115 |
116 | Flutter框架是一个分层的结构,每个层都建立在前一层之上。
117 |
118 | 
119 |
120 | 框架没什么可介绍的(主要是详细介绍我也没找到啥资料,大写的尴尬),看着很简单,就分为两个部分,Framework和Engine部分,其中Framework提供了各种基础的组件库,Engine部分渲染各种widget,两者共同作用,使得运行性能高效稳定。
121 |
122 | ## Flutter调研
123 |
124 | ### 生态
125 |
126 | 在Flutter官方的[Pub](https://pub.dartlang.org/)平台上,纯Flutter Package大概有两千多个,基本上常见的库还是都有的,例如网络、图片、音视频播放等等。但是对于目前Android以及iOS的生态,还是远远的不足的,对于一些复杂的UI或者一些不是特别通用的功能,就得自己去实现。
127 |
128 | ### 包大小
129 |
130 | 根据官网的介绍,一个最小的Android版本的Flutter应用。release版本大小约6.7MB,其中核心引擎大约3.3MB,框架+应用程序代码大约是1.25MB,LICENSE文件(包含在app.flx中)是55k,必需的Java代码.dex为40k,并且约有2.1MB的ICU数据。考虑到目前网络环境,包大小的增加,还也在可以接受的范围。
131 |
132 | ### Crash
133 |
134 | iOS运行官方的例子,会有时候crash掉,因此我们将一个[开源的Flutter应用](https://github.com/roughike/inKino),添加了Bugly上报,在Android平台进行了众测。
135 |
136 | 
137 |
138 | 参与人次大概150人左右,启动次数大概500次左右,没有出现一次Crash数据上报,由于app很简单,并不能说明很多问题,但是众测用户反馈了约12条信息,其中1条是类似于ANR,无法操作,其余的部分则是卡顿相关的反馈。
139 |
140 | ### 流畅性
141 |
142 | 将官方的例子发给测试同学,让在iOS以及Android平台的不同机子上运行了下。在iOS上基本上流畅运行,没有出现卡顿的现象,在Android部分设备上,出现了卡顿的现象。
143 |
144 | 
145 |
146 | 
147 |
148 | 由于没有复杂的例子,其实这个流畅性的测试,意义不是特别大,官方简单的控件展示demo程序,本身就很简单,但是在Android上还是出现了不少问题,只能说明整体还有非常大的优化空间。
149 |
150 | ### 编写复杂程度
151 |
152 | 试着照着一张设计稿进行了简单的纯布局代码工作,初次接触用起来还是比较复杂,尤其是那恐怖的嵌套层级,对代码维护来说绝对是个问题,而且由于Flutter的widget机制,很多组件只支持最基本的操作,例如一些扩展的属性,都得自己去实现,况且现在组件库还不是非常的丰富。代码量也比较多,整个代码大概有500行左右,还只是不涉及到一些交互以及数据绑定等。
153 |
154 | 
155 |
156 | 从运行效果看,还是比较的不错,两者还原的效果都挺不错的。
157 |
158 | ### 结论
159 |
160 | 如果是个人而言,我觉得可以放心大胆的去学习尝试,如果自己开发app的话,可以写一套代码,在多个平台运行发布。
161 |
162 | 如果是商业团队,这个就要自行取舍,目前而言,Flutter生态还是非常的不完善,相关的资料也非常少。目前处于beta 3阶段,多久能到release,能否到release,都是个未知数,而且,用Flutter,最大的风险,就是项目整体的不可把控,一旦出现一些坑,如果能填好,那还行,如果涉及到无法解决的问题,就只能放弃。因此看自己团队人力以及时间合理安排比较合适。目前看阿里的咸鱼团队在研究Flutter。
163 |
164 | 如果单纯从Flutter本身能够解决的问题的方面出发,使用Flutter确实能够产生一定的收益,节省开发成本,如果考虑到目前坑比较多的状况,加上踩坑的时间,可能就无法去评估了。
165 |
166 | 总体来说,Flutter确实是一个比较不错的东西,如果谷歌能够把它发展的比较完善,对于个人以及小团队来说,确实是个福音。
167 |
168 | ## 参考
169 |
170 | 1. [Flutter中文网](https://flutterchina.club/)
171 | 2. [Google 悄悄开发的全新操作系统 Fuchsia 被发现了!](http://osp.io/archives/2540)
172 | 3. [为什么Flutter会选择 Dart ?](http://www.infoq.com/cn/articles/why-flutter-uses-dart)
173 | 4. [Flutter教程(二) 了解Dart语言](https://juejin.im/post/5aebc5fb518825670c45c91b)
174 | 5. [为什么移动端跨平台开发不靠谱?](https://juejin.im/post/59f2346df265da430d573fd8)
175 | 6. [为什么说Flutter是革命性的?](http://www.infoq.com/cn/articles/why-is-flutter-revolutionary)
176 |
177 |
--------------------------------------------------------------------------------
/post/10. Flutter 布局(六)- SizedOverflowBox、Transform、CustomSingleChildLayout详解.md:
--------------------------------------------------------------------------------
1 | # Flutter 布局(六)- SizedOverflowBox、Transform、CustomSingleChildLayout详解
2 |
3 | > 本文主要介绍Flutter布局中的SizedOverflowBox、Transform、CustomSingleChildLayout三种控件,详细介绍了其布局行为以及使用场景,并对源码进行了分析。
4 |
5 | ## 1. SizedOverflowBox
6 |
7 | > A widget that is a specific size but passes its original constraints through to its child, which will probably overflow.
8 |
9 | ### 1.1 简介
10 |
11 | 光看名称,就可以猜出,SizedOverflowBox是SizedBox与OverflowBox的结合体。
12 |
13 | ### 1.2 布局行为
14 |
15 | SizedOverflowBox主要的布局行为有两点:
16 |
17 | * 尺寸部分。通过将自身的固定尺寸,传递给child,来达到控制child尺寸的目的;
18 | * 超出部分。可以突破父节点尺寸的限制,超出部分也可以被渲染显示,与OverflowBox类似。
19 |
20 | ### 1.3 继承关系
21 |
22 | ```
23 | Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > SizedOverflowBox
24 | ```
25 |
26 | ### 1.4 示例代码
27 |
28 | ```
29 | Container(
30 | color: Colors.green,
31 | alignment: Alignment.topRight,
32 | width: 200.0,
33 | height: 200.0,
34 | padding: EdgeInsets.all(5.0),
35 | child: SizedOverflowBox(
36 | size: Size(100.0, 200.0),
37 | child: Container(color: Colors.red, width: 200.0, height: 100.0,),
38 | ),
39 | );
40 | ```
41 |
42 | 代码运行的时候报出了下面的异常,很神奇。但是同学们可以自己看看代码运行的效果,可以超出,但是不太好用。
43 |
44 | ```
45 | ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
46 | ```
47 |
48 | ### 1.5 源码解析
49 |
50 | ```
51 | const SizedOverflowBox({
52 | Key key,
53 | @required this.size,
54 | this.alignment = Alignment.center,
55 | Widget child,
56 | })
57 | ```
58 |
59 | #### 1.5.1 属性解析
60 |
61 | **size**:固定的尺寸。
62 |
63 | **alignment**:对齐方式。
64 |
65 |
66 | #### 1.5.2 源码
67 |
68 | 直接上布局相关的代码:
69 |
70 | ```
71 | size = constraints.constrain(_requestedSize);
72 | if (child != null) {
73 | child.layout(constraints);
74 | alignChild();
75 | }
76 | ```
77 |
78 | 如果child存在的话,就将child设为对应的尺寸,然后按照对齐方式进行对齐。但是在实际写sample的时候,感觉跟我预想中的表现不太一致,而且经常报出异常。不知道是我理解错了,还是样例写错了,如果有了解的同学,麻烦告知,在此感谢。
79 |
80 | ### 1.6 使用场景
81 |
82 | 代码的表现跟我预想中的不太一致,更推荐使用OverflowBox,场景也跟其比较一致。
83 |
84 | ## 2. Transform
85 |
86 | > A widget that applies a transformation before painting its child.
87 |
88 | ### 2.1 简介
89 |
90 | Transform在介绍Container的时候有提到过,就是做矩阵变换的。Container中矩阵变换就是使用的Transform。
91 |
92 | ### 2.2 布局行为
93 |
94 | 有过其他平台经验的,对Transform应该不会陌生。可以对child做平移、旋转、缩放等操作。
95 |
96 | ### 2.3 继承关系
97 |
98 | ```
99 | Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Transform
100 | ```
101 |
102 | ### 2.4 示例代码
103 |
104 | ```
105 | Center(
106 | child: Transform(
107 | transform: Matrix4.rotationZ(0.3),
108 | child: Container(
109 | color: Colors.blue,
110 | width: 100.0,
111 | height: 100.0,
112 | ),
113 | ),
114 | )
115 | ```
116 |
117 | 示例中将Container绕z轴旋转了,代码很简单。Matrix4也提供了很多便捷的构造函数供大家使用,因此写起来,并不会有太大的难度。
118 |
119 | ### 2.5 源码解析
120 |
121 | ```
122 | const Transform({
123 | Key key,
124 | @required this.transform,
125 | this.origin,
126 | this.alignment,
127 | this.transformHitTests = true,
128 | Widget child,
129 | })
130 | ```
131 |
132 | 上面是其默认的构造函数,Transform也提供下面三种构造函数:
133 |
134 | ```
135 | Transform.rotate
136 | Transform.translate
137 | Transform.scale
138 | ```
139 |
140 | #### 2.5.1 属性解析
141 |
142 | **transform**:一个4x4的矩阵。不难发现,其他平台的变换矩阵也都是四阶的。一些复合操作,仅靠三维是不够的,必须采用额外的一维来补充,感兴趣的同学可以自行搜索了解。
143 |
144 | **origin**:旋转点,相对于左上角顶点的偏移。默认旋转点事左上角顶点。
145 |
146 | **alignment**:对齐方式。
147 |
148 | **transformHitTests**:点击区域是否也做相应的改变。
149 |
150 | #### 2.5.2 源码
151 |
152 | 我们来看看它的绘制代码:
153 |
154 | ```
155 | if (child != null) {
156 | final Matrix4 transform = _effectiveTransform;
157 | final Offset childOffset = MatrixUtils.getAsTranslation(transform);
158 | if (childOffset == null)
159 | context.pushTransform(needsCompositing, offset, transform, super.paint);
160 | else
161 | super.paint(context, offset + childOffset);
162 | }
163 | ```
164 |
165 | 整个绘制代码不复杂,如果child有偏移的话,则将两个偏移相加,进行绘制。如果child没有偏移的话,则按照设置的offset、transform进行绘制。
166 |
167 |
168 | ### 2.6 使用场景
169 |
170 | 这个控件算是较常见的控件,很多平移、旋转、缩放都可以使用的到。如果只是单纯的进行变换的话,用Transform比用Container效率会更高。
171 |
172 | ## 3. CustomSingleChildLayout
173 |
174 | > A widget that defers the layout of its single child to a delegate.
175 |
176 | ### 3.1 简介
177 |
178 | 一个通过外部传入的布局行为,来进行布局的控件,不同于其他固定布局的控件,我们自定义一些单节点布局控件的时候,可以考虑使用它。
179 |
180 | ### 3.2 布局行为
181 |
182 | CustomSingleChildLayout提供了一个控制child布局的delegate,这个delegate可以控制这些因素:
183 |
184 | * 可以控制child的布局constraints;
185 | * 可以控制child的位置;
186 | * 在parent的尺寸不依赖于child的情况下,可以决定parent的尺寸。
187 |
188 | ### 3.3 继承关系
189 |
190 | ```
191 | Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > CustomSingleChildLayout
192 | ```
193 |
194 | ### 3.4 示例代码
195 |
196 | ```
197 | class FixedSizeLayoutDelegate extends SingleChildLayoutDelegate {
198 | FixedSizeLayoutDelegate(this.size);
199 |
200 | final Size size;
201 |
202 | @override
203 | Size getSize(BoxConstraints constraints) => size;
204 |
205 | @override
206 | BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
207 | return new BoxConstraints.tight(size);
208 | }
209 |
210 | @override
211 | bool shouldRelayout(FixedSizeLayoutDelegate oldDelegate) {
212 | return size != oldDelegate.size;
213 | }
214 | }
215 |
216 | Container(
217 | color: Colors.blue,
218 | padding: const EdgeInsets.all(5.0),
219 | child: CustomSingleChildLayout(
220 | delegate: FixedSizeLayoutDelegate(Size(200.0, 200.0)),
221 | child: Container(
222 | color: Colors.red,
223 | width: 100.0,
224 | height: 300.0,
225 | ),
226 | ),
227 | )
228 | ```
229 |
230 | 由于SingleChildLayoutDelegate是一个抽象类,我们需要单独写一个继承自它的delegate,来进行相关的布局操作。上面例子中是一个固定尺寸的布局。
231 |
232 | ### 3.5 源码解析
233 |
234 | 构造函数如下:
235 |
236 | ```
237 | const CustomSingleChildLayout({
238 | Key key,
239 | @required this.delegate,
240 | Widget child
241 | })
242 | ```
243 |
244 | #### 3.5.1 属性解析
245 |
246 | **alignment**:一个抽象类,我们需要自行实现布局的类。
247 |
248 | #### 3.5.2 源码
249 |
250 | 我们直接来看其布局函数:
251 |
252 | ```
253 | size = _getSize(constraints);
254 | if (child != null) {
255 | final BoxConstraints childConstraints = delegate.getConstraintsForChild(constraints);
256 | assert(childConstraints.debugAssertIsValid(isAppliedConstraint: true));
257 | child.layout(childConstraints, parentUsesSize: !childConstraints.isTight);
258 | final BoxParentData childParentData = child.parentData;
259 | childParentData.offset = delegate.getPositionForChild(size, childConstraints.isTight ? childConstraints.smallest : child.size);
260 | }
261 | ```
262 |
263 | 其child的constraints是通过delegate传入的,而这个delegate则是我们通过外部继承自SingleChildLayoutDelegate实现的。
264 |
265 | 我们接下来看一下SingleChildLayoutDelegate提供了哪些接口:
266 |
267 | ```
268 | Size getSize(BoxConstraints constraints) => constraints.biggest;
269 | BoxConstraints getConstraintsForChild(BoxConstraints constraints) => constraints;
270 | Offset getPositionForChild(Size size, Size childSize) => Offset.zero;
271 | bool shouldRelayout(covariant SingleChildLayoutDelegate oldDelegate);
272 | ```
273 |
274 | 其中前三个都是布局相关的,包括尺寸、constraints、位置。最后一个函数是判断是否需要重新布局的。我们在编写delegate的时候,可以选择需要进行修改的属性进行重写,并给予相应的布局属性即可。
275 |
276 | ### 3.6 使用场景
277 |
278 | 这种控件用起来可能会繁琐一些,但是我们可以通过这个控件去封装一些基础的控件,供他人使用。
279 |
280 | ## 4. 阶段性小结
281 |
282 | 到目前为止,Flutter中单子节点布局控件,大致上都简单的梳理了一遍。如果一直在关注这个系列文章的同学,应该可以发现我经常吐槽其控件设计。
283 |
284 | Flutter中总共有18个单子节点布局控件,18个啊,还没有算多子节点布局控件,也没有算可能会新增的。这样的学习成本非常高。虽然常见的就那么几种,平时一直用那些也都没有问题,但是布局的时候总是会遇到那么几种解决不了的问题。而且梳理的时候,会发现,几种控件都能解决的问题,并不是说把效果实现出了就完事了,这中间肯定会涉及到哪种控件效率更高。如果要对Flutter项目做较深入的性能优化,这些控件肯定都得掌握。
285 |
286 | Flutter的这种设计,把一些原本应该由它们承担的成本,转移到了开发者身上。很多控件在日常使用中几乎都用不上,并没有考虑太多实际的使用场景。当然了,也还是得安慰自己,这毕竟只是初期,乱点就乱点,日子肯定会越来越好的。
287 |
288 | 后面还会将多子节点控件全部梳理一遍,然后来个大总结,对什么场景该使用哪种控件,如何进行控件级别的优化,做一个总结。
289 |
290 | ## 5. 后话
291 |
292 | 笔者建了一个Flutter学习相关的项目,[Github地址](https://github.com/yang7229693/flutter-study),里面包含了笔者写的关于Flutter学习相关的一些文章,会定期更新,也会上传一些学习Demo,欢迎大家关注。
293 |
294 | ## 6. 参考
295 |
296 | 1. [SizedOverflowBox class](https://docs.flutter.io/flutter/widgets/SizedOverflowBox-class.html)
297 | 2. [Transform class](https://docs.flutter.io/flutter/widgets/Transform-class.html)
298 | 3. [CustomSingleChildLayout class](https://docs.flutter.io/flutter/widgets/CustomSingleChildLayout-class.html)
299 |
300 |
301 |
--------------------------------------------------------------------------------
/post/16. Flutter 动画详解(一).md:
--------------------------------------------------------------------------------
1 | # Flutter 动画详解(一)
2 |
3 | > 本文主要介绍了动画的原理相关概念,对其他平台的动画做了一个简要的梳理,并简要的介绍了Flutter动画的一些知识。
4 |
5 | ## 1. 动画介绍
6 |
7 | 动画对于App来说,非常的重要。很多App,正是因为有了动画,所以才会觉得炫酷。移动端的动画库有非常的多,例如iOS上的Pop、web端的animate.css、Android端的AndroidViewAnimations、跨平台的Lottie等。正是因为有了这些封装好的动画库,我们制作酷炫的效果方便了不少。当然了,这些库都是基于各平台基础的动画API实现的,笔者今天要聊的,也就是基础的动画及背后的原理。
8 |
9 | ### 1.1 动画的本质
10 |
11 | 动画顾名思义,就是动起来的画面。画面为什么会动起来了呢?在回答这个问题之前,我们先引入一个概念。
12 |
13 | > 人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,视觉的这一现象则被称为“视觉暂留”。
14 |
15 | 视觉暂留被认为是电影的最重要的一个理论基础。我们看到的动画,实际上是一连串的画面组成,只不过是以很快的速度去播放,人眼在下一个画面出来之前,还残留着上一个画面的视觉,看起来就像是在没有间隔的播放这一系列的图片,也就是我们称之为的动画。
16 |
17 | ### 1.2 相关概念
18 |
19 | 动画会有很多相关的概念,理解了这些概念,会对实际的使用更有帮助。
20 |
21 | #### 1.2.1 帧
22 |
23 | 刚才在介绍动画本质的时候,用到了画面这个词汇,只是方便读者去理解,这个画面,在学术上叫做`帧`。
24 |
25 | > 帧就是影像动画中最小单位的单幅影像画面,一帧就是一副静止的画面。
26 |
27 | 
28 |
29 | 帧里面又分为关键帧和过渡帧,这两概念是理解一些动画的基础,例如Android中的补间动画。在一些场景中,我们可能不会给出一个动画的所有帧,所以将帧分成关键帧和过渡帧。关键帧可以理解为一个动画的起始状态,而过渡帧则是系统自动完成插在关键帧之间的部分。
30 |
31 | 
32 |
33 | 我们知道Android中的补间动画,基础的有四种类型,平移、缩放、旋转、透明度。而我们设置动画的时候,通常只是设置起始的状态,也就是关键帧,中间过程其实我们并不需要去考虑,如果关注动画速率的话,顶多加一个差值器去控制,但是中间生成的帧我们并没有提供。
34 |
35 | 系统为什么能够补齐过渡帧呢?我们看下这四种基本的动画类型,给定起始状态,中间状态我们其实是可以通过计算推演出来的,这也是系统为什么能够补齐的原因。
36 |
37 | 是不是只有这四种才可以通过系统填补过渡帧呢?显然不是的,例如一个跳跃前进的动画,添加一些限制条件,就可以推演出中间的状态。系统提供的只是比较常见的四种,并不是说只有这四种,而是绝大部分动画都可以通过这四种组合实现。当然了,肯定也是有实现不了的,这个时候有一个办法就是通过canvas画出来。
38 |
39 | 另外再插一嘴,Android系统提供的四种动画操作,也是变换矩阵是四维的原因,具体的就不多说了,之前文章也有介绍过。
40 |
41 | 最后一嘴,此处讲解帧的概念,拿了很多Android相关的知识去讲解,只是希望读者能够通过一些已知的概念,去理解一些未知的。动画的原理都一样,具体到某个平台,可能顶多就是实现或者叫法不一样罢了。
42 |
43 | #### 1.2.2 帧数与FPS
44 |
45 | 小时候很多人都玩过书角动画。在书或者本子的一角,每一页都画上一个画面,然后拨书角,不同速度拨,动画的感受不一样,拨的越快,动画越流畅。这是为什么呢?这就牵扯到帧数与FPS了。
46 |
47 | > 帧数,帧的数量。FPS(Frame per Second),即每秒显示帧数。
48 |
49 | 这两个概念,主要是FPS有什么作用呢?这是因为人眼生理构造的原因。人眼残留镜像的时间是有限的,如果过了这个时间,下一帧还没有变化,就会感觉不流畅。但也不是帧数越大越好,毕竟人眼也是有极限的。
50 |
51 | 
52 |
53 | #### 1.2.3 插值器
54 |
55 | 如果动画播放一直都是这种匀速的进行,那表现形式就太单一了。那如何实现非线性的动画效果呢,这个时候就需要用到插值器了。
56 |
57 | 插值器其实并不复杂,就是一个数学函数,设置属性值从初始值过渡到结束值的变化规律。每个平台都有自己定义好的一系列插值器,可以供开发者选择使用,也提供自定义的接口,本质上是一个贝塞尔函数。
58 |
59 | 一个匀速插值器如下:
60 |
61 | ```
62 | 属性值百分比 = 时间百分比
63 | ```
64 |
65 | ### 1.3 如何实现
66 |
67 | 动画的基本原理和一些基本概念都介绍了一下,现在来聊一下动画的实现。
68 |
69 | 先抛开系统层级的各种渲染优化,也仅仅是以补间动画为例,假设以现有的移动平台基础上,去实现一套简单的动画框架,该如何去实现呢?
70 |
71 | 以Android的为例,要实现平移、缩放、旋转、透明度这四种基础的补间动画,可以看到,这些都是基于某个属性的动画,平移是基于point、缩放是基于scale、旋转是基于angle、透明度是基于alpha。
72 |
73 | 结合插值器,提炼出一个通用的动画类,这个类的作用是根据插值器,得到视图某个时间点的属性变化的状态。
74 |
75 | 既然各个时间点的状态已经有了,剩下来的就是让各个状态渲染出来。底层的机制在此处不去讨论,这个地方就需要一个定时器,定时器的作用是每隔一段时间就把素材渲染到屏幕上。
76 |
77 | 至此,一个简易的动画框架就出来了。如果对各平台比较了解的话,就知道我说的是视图动画,真正的动画引擎不是这么简单,涉及到的技术也比较复杂,但是大体的思想不会有错,不管是哪种动画,都是跟时间相关的帧序列,只是实现方式不同。
78 |
79 | 这也是笔者为什么花这么多篇幅去介绍动画相关的概念,知道一些底层原理后,不管什么平台,怎么去实现,底层的思想肯定都差不多,只是实现上的不同。
80 |
81 | ## 2. 其他平台的动画
82 |
83 | Flutter动画,与Android、iOS等平台对比,其实本身并没有什么特别之处。基本的原理是一样的,只是提供的种类以及实现的方式不同罢了。
84 |
85 | ### 2.1 Android动画
86 |
87 | Android的动画,大的分类有两种:
88 |
89 | * 视图动画(View Animation)
90 | * 属性动画(Property Animation)
91 |
92 | 视图动画又可以分为两类:
93 |
94 | * 补间动画(Tween Animation)
95 | * 逐帧动画(Frame Animation)
96 |
97 | 这之间的差别是什么呢?它们只有实现上的差别
98 |
99 | * 补间动画是根据初始状态,系统自动补充中间状态;
100 | * 逐帧动画则是需要我们提供每一帧;
101 | * 视图动画只是作用于视图上,而不会改变控件的属性;
102 | * 属性动画则是会实实在在的更改控件的属性。
103 |
104 | 可以看出Android的动画分类还是比较明晰的。
105 |
106 | ### 2.2 iOS动画
107 |
108 | iOS很久没弄了,在这里也简单说下,不对的话还请各位指正。
109 |
110 | * 隐式动画
111 | * 显式动画
112 |
113 | 显式动画又可以分为两类:
114 |
115 | * 基础动画
116 | * 关键帧动画
117 |
118 | 这些动画类别之间的差别是什么呢?
119 |
120 | * 隐式动画,顾名思义是不指定动画类型,更改某个属性,Core Animation来决定如何且何时去做动画;
121 | * 基础动画,根据起始值来做动画;
122 | * 关键帧动画,则是定义一系列关键帧,系统自动补齐中间的过渡帧。
123 |
124 | 通过动画这一块儿,可以看出iOS的分类其实是比较的模糊的,有一些历史包袱。
125 |
126 | ### 2.3 css动画
127 |
128 | css动画大体上有两种:
129 |
130 | * Transition
131 | * Animation
132 |
133 | web中的动画分类简单的多了
134 |
135 | * Transition动画,给定起始值,可以结合插值器做动画;
136 | * Animation动画,则是定义一系列关键帧,系统补齐中间的过渡帧。
137 |
138 | ### 2.4 小节
139 |
140 | 通过上面个平台动画粗略的介绍,动画在不同平台虽然被叫着不同的名称,本质上其实都差不多的,变来变去都是这几种方式,要么根据属性要么根据关键帧,要么更改绘制层,要么更改控件本身属性。一些游戏引擎,虽然我没有看,但是我觉得原理也大致相似。
141 |
142 | ## 3. Flutter动画
143 |
144 | 上面铺垫了这么多,终于到Flutter动画了。Flutter是一门比较新的技术,历史包袱理应说是最小的。
145 |
146 | ### 3.1 Flutter动画分类
147 |
148 | Flutter动画分为两类:
149 |
150 | * 补间动画(Tween Animation)
151 | * 基于物理的动画(Physics-based animation)
152 |
153 | 补间动画很好理解,基于物理的动画是这个什么鬼。
154 |
155 | > 基于物理的动画是一种遵循物理学定律的动画形式
156 |
157 | 举个例子,比方说你滑动一张图片,这个过程不是匀速的,而是起始速度快,然后慢慢的降速,就像一本书在地上往前推一样。它有什么特点呢?
158 |
159 | * 遵循物理学定律;
160 | * 能够依据加速度和速度去计算和更新每一帧的动画数值;
161 | * 当受力平衡时,动画为处于恒定运动或静止状态。
162 |
163 | 哈哈,最后一点是不是似曾相识,这样做的好处是什么呢?随着人们生活水平的极大提升,移动端硬件这些年也是赶英超美,人们不再满足于简单的动画,于是就有部分有(xian)识(de)之(dan)士(teng),实现了基于物理学定律的动画。
164 |
165 | 这种动画iOS或者Android有没有呢,是有的,但不是作为最基础的动画API被提供。为什么其他平台没有将这个纳入最基本的动画中去呢?
166 |
167 | * 历史原因。iOS以及Android端多年前就诞生了,那个时候,硬件资源都是极其有限的,当时的环境不足以支撑这种动画效果。但也不是说没有,一些游戏引擎里面也是有的,但是作为操作系统,把这些集成进去,还是不太现实的。
168 | * 认知过程。电脑以及移动端这些年的发展,最开始只是满足于查看最简单的文本,然后各种图片视频。随着互联网的越来越普及,人们的需求越来越多了。于是,在一些游戏里面才会见到的基于物理学定律的动画,进入了寻常百姓家。反观一下,现在也是有非常多的“委屈”事物。例如人类几千年都是通过人眼看现实的事物,现在却被限制在一个小屏幕上,这其实是不合理的。所以AR、VR,还有一些动画片科幻片中的远程感知等技术,才会层出不穷,当然这个扯的有些远了。
169 |
170 | 基于物理的动画这么好,那有什么好处呢?更自然,更加符合人们的认知。
171 |
172 | ### 3.2 分类的原因
173 |
174 | 前面讲的各平台的动画,从本质上看,基于某个属性也好,帧动画也好,都是从一种状态到另一种状态,而中间过程是可以推演出来的,所以Flutter提供补间动画。
175 |
176 | 基于物理的动画,我猜测可能是为了实现其他平台上的一些效果,例如弹簧、阻尼效果等等。所以Flutter就提供了这种动画API,毕竟没什么包袱。
177 |
178 | ### 3.3 动画模式
179 |
180 | Flutter提炼了三种动画模式,与其说提炼出来的,倒不如说统一不能更为合适。
181 |
182 | * list、grid中的动画(Animated list or grid)。场景是item的添加或者删除操作;
183 | * 转场动画(Shared element transition)。场景是当前页面打开另一页面的过渡动画;
184 | * 交错动画(Staggered animation)。场景是需要部分或者完全交错的动画。
185 |
186 | ### 3.4 复杂度
187 |
188 | Flutter的实现原理以及这个阶段,注定了做动画是非常麻烦的一件事情。跨平台的技术做动画都麻烦,这个似乎是通识,为了跨平台而同化的一些东西,到异化部分,就变得蛋疼了,动画正是这种存在。
189 |
190 | Flutter做动画复杂体现在哪些地方呢?
191 |
192 | * 实现的动画较少,这个是初期,没啥好说的;
193 | * 动画实现的方式复杂,这个是Flutter的设计思想所决定的。
194 |
195 | ## 4. 小节
196 |
197 | 关于动画的具体的实现、一些底层的代码逻辑以及如何使用,将会在下一篇文章中做介绍。这篇文章更多的是偏于一些普适性的介绍,关于Flutter动画相关的介绍反而很少。希望读者能够了解一些动画的原理,以及各个平台动画的大致实现方式,这样可以更好的理解Flutter动画的设计思想。文中若有错误的地方,还恳请指出,在此不胜感激。
198 |
199 | ## 5. 后话
200 |
201 | 笔者建了一个Flutter学习相关的项目,[Github地址](https://github.com/yang7229693/flutter-study),里面包含了笔者写的关于Flutter学习相关的一些文章,会定期更新,也会上传一些学习Demo,欢迎大家关注。
202 |
203 | ## 6. 参考
204 |
205 | 1. [电影原理](https://baike.baidu.com/item/%E7%94%B5%E5%BD%B1%E5%8E%9F%E7%90%86/4718189)
206 | 2. [视觉暂留](https://baike.baidu.com/item/%E8%A7%86%E8%A7%89%E6%9A%82%E7%95%99/5125149)
207 | 3. [iOS-Core-Animation-Advanced-Techniques](https://github.com/AttackOnDobby/iOS-Core-Animation-Advanced-Techniques)
208 | 4. [CSS动画简介](http://www.ruanyifeng.com/blog/2014/02/css_transition_and_animation.html)
209 | 5. [Animations in Flutter](https://flutter.io/animations/)
210 | 6. [Android 中基于物理特性的动画简介](https://zhuanlan.zhihu.com/p/28239508)
211 |
--------------------------------------------------------------------------------
/post/17. Flutter 动画详解(二).md:
--------------------------------------------------------------------------------
1 | # Flutter 动画详解(二)
2 |
3 | > 本文通过代码层面去分析Flutter动画的实现过程,介绍了Flutter中的Animation库以及Physics库。
4 |
5 | ## 1. 介绍
6 |
7 | 本文会从代码层面去介绍Flutter动画,因此不会涉及到Flutter动画的具体使用。
8 |
9 | ### 1.1 Animation库
10 |
11 | Flutter的animation库只依赖两个库,Dart库以及physics库。animation是采用Dart编写的,所以依赖Dart库是很正常的。physics库是什么呢?
12 |
13 | > Simple one-dimensional physics simulations, such as springs, friction, and gravity, for use in user interface animations.
14 |
15 | physics库是一个简单的物理模拟的库,包含弹簧、阻尼、重力等物理效果。前篇文章介绍过Flutter动画,Flutter动画两个分类中的一个就是基于物理的动画(Physics-based animation)。所以可以猜测出animation库中有一部分代码,是实现了另一种动画--补间动画(Tween Animation)。
16 |
17 | 通过这种库的划分,也可以大致猜测出,基于物理动画的库是后续添加的。这说明了什么呢?
18 |
19 | * 补间动画是现代移动端相对基础的动画类型,这个是必须的;
20 | * 基于物理动画是在体验上的改善添加上去的,大致可以猜测出为iOS端的体验优化;
21 |
22 | ### 1.2 Physics库
23 |
24 | Flutter基于物理的动画,实际上是相当简单的。目前实现了弹簧、阻尼、重力三种物理效果,整个库的代码量也不多。详细的代码在下面的部分介绍,在此处,我们先来说下基于物理的动画库的原理。
25 |
26 | 基于物理的动画,给我们的感觉会更真实,这是因为其更符合人们日常生活的感官。例如做一个球体下落的动画,如果是匀速的下落,给人的感觉会不够真实,实际的生活经验告诉我们,球体自由下落应该是会有先慢后快的一个过程。如果让我们自己去实现这么一个动画效果,我们会怎么去处理呢?
27 |
28 | 高中物理我们学习过自由落体相关的概念,其中的位移计算公式:
29 |
30 | > s = 1/2 * g * t * t
31 |
32 | 从公式中我们知道,自由落体的位移跟时间不是线性关系。我们可以根据这个公式,来实时的计算出位移来。
33 |
34 | 如果是摩擦阻尼或者弹簧呢,也都有相关的物理公式,我们所谓的基于物理的动画库,也就是基于此类公式来实现的,本质上还是补间动画,只不过过程遵循物理规律比较复杂罢了。
35 |
36 | ## 2. Animation库
37 |
38 | 讲解这一部分,也考虑过将Flutter的动画原理先介绍一下。想了想,前一篇文章介绍过这些普世的动画原理,Flutter只不过是特定平台的实现,无非是实现手段的不同,因此,Flutter动画原理的解析,放到本文最后的小节部分,在代码的基础上去解释,笔者觉得更加好理解。
39 |
40 | ### 2.1 animation.dart
41 |
42 | animation.dart定义了动画的四种状态,以及核心的抽象类Animation。
43 |
44 | #### 2.1.1 动画的四种状态
45 |
46 | 这个文件中定义了Animation的四种状态:
47 |
48 | * dismissed:动画的初始状态
49 | * forward:从头到尾播放动画
50 | * reverse:从尾到头播放动画
51 | * completed:动画完成的状态
52 |
53 | #### 2.1.2 Animation类
54 |
55 | Animation类是Flutter动画中核心的抽象类,它包含动画的当前值和状态两个属性。定义了动画的一系列回调,
56 |
57 | * 动画过程中值变化的回调:
58 |
59 | ```
60 | void addListener(VoidCallback listener);
61 | void removeListener(VoidCallback listener);
62 | ```
63 |
64 | * 状态的回调函数:
65 |
66 | ```
67 | void addStatusListener(AnimationStatusListener listener);
68 | void removeStatusListener(AnimationStatusListener listener);
69 | ```
70 |
71 | ### 2.2 curve.dart
72 |
73 | > A curve must map t=0.0 to 0.0 and t=1.0 to 1.0.
74 |
75 | 看到这段英文,首先会想到什么?没错,插值器。Curve也是一个抽象类,定义了时间与数值的一个接口。
76 |
77 | ```
78 | double transform(double t);
79 | ```
80 |
81 | 例如一个线性的插值器,实现代码如下。
82 |
83 | ```
84 | class _Linear extends Curve {
85 | const _Linear._();
86 |
87 | @override
88 | double transform(double t) => t;
89 | }
90 | ```
91 |
92 | 该文件下面定义了非常多类型的插值器,具体的实现不一一展开了。Flutter定义了一系列的插值器,封装在Curves类中,有下面13种效果。
93 |
94 | * linear
95 | * decelerate
96 | * ease
97 | * easeIn
98 | * easeOut
99 | * easeInOut
100 | * fastOutSlowIn
101 | * bounceIn
102 | * bounceOut
103 | * bounceInOut
104 | * elasticIn
105 | * elasticOut
106 | * elasticInOut
107 |
108 | 如果上面的13种还不满足需求的话,还可以使用Cubic类来进行自定义的构造。可以看出这块儿实现参考了web中的相关实现。
109 |
110 | ### 2.3 tween.dart
111 |
112 | 该文件定义了一系列的估值器,Flutter通过抽象类Animatable来实现估值器。关于Animatable,我们可以先看下其定义。
113 |
114 | > An object that can produce a value of type `T` given an [Animation]
115 | as input.
116 |
117 | 可以根据不同的输入,产出不同的数值。通过重载下面的函数来产生不同的估值器。
118 |
119 | ```
120 | T transform(double t);
121 | ```
122 |
123 | 它的最主要的子类是Tween,一个线性的估值器,实现如下,非常的简单,就是一个线性函数。
124 |
125 | ```
126 | T lerp(double t) {
127 | assert(begin != null);
128 | assert(end != null);
129 | return begin + (end - begin) * t;
130 | }
131 |
132 | @override
133 | T transform(double t) {
134 | if (t == 0.0)
135 | return begin;
136 | if (t == 1.0)
137 | return end;
138 | return lerp(t);
139 | }
140 | ```
141 |
142 | 在Tween的基础上实现了不同类型的估值器。
143 |
144 | * ReverseTween
145 | * ColorTween
146 | * SizeTween
147 | * RectTween
148 | * IntTween
149 | * StepTween
150 | * ConstantTween
151 |
152 | 还可以通过自定义的插值器去实现估值器,例如通过curve实现的估值器CurveTween。
153 |
154 | ### 2.4 animation_controller.dart
155 |
156 | 动画的控制,就在这个文件下面实现,其中最重要的部分是AnimationController,它派生自Animation类。
157 |
158 | AnimationController的功能有如下几种:
159 |
160 | * 播放一个动画(forwaed或者reverse),或者停止一个动画;
161 | * 设置动画的值;
162 | * 设置动画的边界值;
163 | * 创建基于物理的动画效果。
164 |
165 | 默认情况下,AnimationController是线性的产生0.0到1.0之间的值,每刷新一帧就产出一个数值。AnimationController在不使用的时候需要dispose,否则会造成资源的泄漏。
166 |
167 | #### 2.4.1 TickerProvider
168 |
169 | 提到AnimationController必须要先说一下TickerProvider。
170 |
171 | > An interface implemented by classes that can vend Ticker objects.
172 |
173 | TickerProvider定义了可以发送Ticker对象的接口,
174 |
175 | ```
176 | Ticker createTicker(TickerCallback onTick);
177 | ```
178 |
179 | Ticker能干什么呢?
180 |
181 | > Tickers can be used by any object that wants to be notified whenever a frame triggers.
182 |
183 | 它的主要作用是获取每一帧刷新的通知,作用就显而易见了,相当于给动画添加了一个动起来的引擎。
184 |
185 | #### 2.4.2 AnimationController
186 |
187 | 现在再次回到AnimationController。上面为什么要先说一下TickerProvider呢,这是因为AnimationController的构造函数中需要一个TickerProvider参数。
188 |
189 | 结合上面介绍的插值器、估值器以及Ticker回调,AnimationController大致的工作流程,我相信很多人都可以理出来了。
190 |
191 | 随着时间的流逝,插值器根据时间产生的值作为输入,提供给估值器,产生动画的实际效果值,结合Ticker的回调,渲染出当前动画值的图像。这也是补间动画的工作原理。
192 |
193 | 
194 |
195 | AnimationController具体的源码不做分析了,可以看到Flutter的动画实现的其实是相当的原始,AnimationController需要一个触发刷新的回调,输出也是值的改变,并不像成熟平台里面的配合View去做动画。
196 |
197 | ## 3. Physics库
198 |
199 | Physics库基本上就是插值器的实现部分,这部分比较简单
200 |
201 | 
202 |
203 | Simulation定义了基于物理动画的相关接口,具体有位置、速度、是否完成以及公差(Tolerance)
204 |
205 | ```
206 | double x(double time);
207 | double dx(double time);
208 | ```
209 |
210 | GravitySimulation的实现如下,其中_a加速度,_x是初始距离,_v是初始速度:
211 |
212 | ```
213 | @override
214 | double x(double time) => _x + _v * time + 0.5 * _a * time * time;
215 |
216 | @override
217 | double dx(double time) => _v + time * _a;
218 | ```
219 |
220 | 相信学过高中物理的读者,对这公式不会陌生。其他几种具体实现不在此处一一展开了哈。如果扩展这个物理动画库的话,也很好去扩展,掌握一些物理公式,就可以去仿照实现了。
221 |
222 | ## 4. Ticker
223 |
224 | 关于动画的驱动,在此简单的说一下,Ticker是被SchedulerBinding所驱动。SchedulerBinding则是监听着Window.onBeginFrame回调。
225 |
226 | Window.onBeginFrame的作用是什么呢,是告诉应用该提供一个scene了,它是被硬件的VSync信号所驱动的。
227 |
228 | 具体可以查看sky_engine下面的window.dart的实现,不做展开了。
229 |
230 | ## 5. 小节
231 |
232 | 本篇文章简单的从代码的层面解析了一下Flutter的动画,更深入的Ticker这块儿,感兴趣的读者可以自行去了解,这块儿涉及到sky_engine下面的代码。
233 |
234 | 本篇文章,笔者依然试图绕过代码去讲解一些普适性的东西,但是Flutter这块儿代码实现的确实挺简单的,造成的问题是调用起来费劲。
235 |
236 | 基于物理的动画,我们要知道深层次的是物理公式,有这个基础,我们才可以制作出符合感官的动画效果。其本质也是补间动画,过程可以被计算出来。
237 |
238 | 可以说的宽泛一些,一般的动画,大部分都是补间动画,如果我们自行去设计一套动画系统,插值器、估值器、驱动部分以及动画的管理部分,这四个模块之间相互协调输出一帧一帧的动画过场。绝大部分平台的动画设计,也都逃不过这些因素,只不过实现的方式各不相同。
239 |
240 | 如果文中有错误的地方,烦请指正,笔者水平有限,再次感谢。
241 |
242 | ## 6. 后话
243 |
244 | 笔者建了一个Flutter学习相关的项目,[Github地址](https://github.com/yang7229693/flutter-study),里面包含了笔者写的关于Flutter学习相关的一些文章,会定期更新,也会上传一些学习Demo,欢迎大家关注。
245 |
246 | ## 7. 参考
247 |
248 | 1. [Animations in Flutter](https://flutter.io/animations/)
249 | 2. [Tutorial: Animations in Flutter](https://flutter.io/tutorials/animation/)
250 | 3. [TickerProvider class](https://docs.flutter.io/flutter/scheduler/TickerProvider-class.html)
251 | 4. [Ticker class](https://docs.flutter.io/flutter/scheduler/Ticker-class.html)
252 | 5. [SchedulerBinding class](https://docs.flutter.io/flutter/scheduler/SchedulerBinding-class.html)
253 | 6. [onBeginFrame property](https://docs.flutter.io/flutter/dart-ui/Window/onBeginFrame.html)
254 |
--------------------------------------------------------------------------------
/post/2. Flutter Plugin开发流程.md:
--------------------------------------------------------------------------------
1 | # Flutter Plugin开发流程
2 |
3 | > 这篇文章主要介绍了Flutter Plugin开发流程,包括如何利用Android Studio开发以及发布等。
4 |
5 | 本文主要给大家介绍如何开发Flutter Plugin中Android的部分。有关Flutter以及Flutter Plugin的概念,感兴趣的可以从官网查看相关资料。
6 |
7 | ## 简介
8 |
9 | 笔者的环境是Mac下Android Studio进行的开发,AS也是谷歌官推的,安装flutter插件后,开发起来相对于其他IDE来说,方便很多,自带了三种模板:
10 |
11 | * Flutter Application: Flutter应用
12 | * Flutter Plugin:Flutter插件
13 | * Flutter Package:纯Dart组件
14 |
15 | `Plugin其实就是一个特殊的Package`。Flutter Plugin提供Android或者iOS的底层封装,在Flutter层提供组件功能,使Flutter可以较方便的调取Native的模块。很多平台相关性或者对于Flutter实现起来比较复杂的部分,都可以封装成Plugin。其原理如下
16 |
17 | 
18 |
19 | 消息在client和host之间通过平台通道(platform channels)来进行的,之间的通讯都是`异步`的。
20 |
21 | ## 创建组件
22 |
23 | 直接在Android Studio中新建一个Flutter Plugin的工程,当然也可以使用命令行来进行,例如创建一个flutter_text_plugin。
24 |
25 | > flutter create --org com.example --plugin flutter_text_plugin
26 |
27 | 如果想支持swift或者kotlin,可以用如下命令进行创建:
28 |
29 | > flutter create --org com.example --plugin -i swift -a kotlin flutter_text_plugin
30 |
31 | 更多的参数选项,大家可以 查看帮助文档,当然还是比较推荐直接用AS进行创建,简单直观。用AS打开项目,可以看到项目的组织结构
32 |
33 | ```
34 | root
35 | android
36 | example
37 | ios
38 | lib
39 | ...
40 | ```
41 |
42 |
43 | android以及ios文件夹是我们将要编写插件的native层的地方,lib文件夹是编写与native层映射的地方,native与flutter之间不能直接通信,必须通过MethodChannel来间接调用。example文件夹则是例子工程,编写的插件可以直接在这个项目中进行验证。在本文中,我们主要在android目录下进行,也就是android部分。
44 |
45 | ## 编写Android部分
46 |
47 | 用AS打开flutter_text_plugin/android项目,这样子开发起来比较方便。但是打开过后,会发现出现了很多错误,提示找不到flutter相关的东西,我们仔细看这个项目,会发现跟我们平时用AS建的Android项目有所不同,少了很多部分,目录也有所不同。这是因为这个android项目不需要能够直接去运行,因此减少了很多东西。但是对于初次接触的人来说,可能是一头懵逼,例如该如何添加第三方库,如何添加proguard rule等等。
48 |
49 | ### 引入flutter库
50 |
51 | android插件工程是没有引入flutter库的,所以才会出现错误提示,我们在项目根目录建立一个libs文件夹,用来存放flutter库。
52 |
53 | flutter库就在我们的flutter sdk中,路径如下
54 |
55 | > /bin/cache/artifacts/engine
56 |
57 | engine下面包含了各种平台的flutter库,我们随便拷贝一个Android平台的库到libs文件夹下,右键flutter.jar,弹出菜单选择`Add As Library...`。
58 |
59 | 经过这一步,项目中不会再报错了,但是,由于整个flutter plugin包含了flutter库,因此不能只是简单的添加就了事了,点击菜单`Project Structure...`,找到flutter_text_plugin的Dependencies中,将flutter库的Scope从Implementation改成`Compile Only`。至此,引入flutter库的工作完成了,可以进行插件的编写操作了。
60 |
61 | ### 添加第三方库
62 |
63 | 添加第三方库有两种,一种是jar包引入,另一种通过gradle的方式进行。由于进行了第一步flutter库的引入,这一步就简单多了。查看build.gradle文件,可以看到最下面出现了如下的信息。
64 |
65 | ```
66 | dependencies {
67 | compileOnly files('libs/flutter.jar')
68 | }
69 | ```
70 |
71 |
72 | 看到这个,是不是就明朗多了,添加静态库以及添加在线库都可以在这个地方进行。例如我添加一个bugly静态库以及okhttp3库:
73 |
74 | ```
75 | dependencies {
76 | compileOnly files('libs/flutter.jar')
77 | implementation 'com.squareup.okhttp3:okhttp:3.10.0'
78 | implementation files('libs/bugly_crash_release.jar')
79 | }
80 | ```
81 |
82 |
83 | ### 添加proguard rule
84 |
85 | 由于了bugly以及okhttp3库,因此需要添加progurad rule。我们发现项目中没有proguard-rules.pro文件,因此这一步也需要我们自己去创建,在根目录下,建立proguard-rules.pro文件,将混淆规则添加进去,然后修改build.gradle文件,添加如下信息,跟普通Android项目差不多:
86 |
87 | ```
88 | buildTypes {
89 | release {
90 | minifyEnabled true
91 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
92 | }
93 | debug {
94 | minifyEnabled false
95 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
96 | }
97 | }
98 | ```
99 |
100 |
101 | ### Android权限
102 |
103 | 添加了bugly以及okhttp3库,需要对应的权限申明,才能正常运行。直接在manifest文件下,添加对应的权限
104 |
105 | ```
106 |
107 |
108 |
109 |
110 |
111 | ```
112 |
113 |
114 | ### 插件开发
115 |
116 | 至此,准备工作都已就绪,你可以把这个项目当做一个独立的Android项目,在上面进行各种封装操作,然后在FlutterTestPlugin文件下,将接口暴露出来。通过platform channels与flutter层关联起来。
117 |
118 | ## 发布
119 |
120 | 当插件开发完毕,可以将插件发布让其他人使用,在发布之前,确保pubspec.yaml,、README.md以及CHANGELOG.md文件的内容都正确填写完毕。可以通过dry-run命令来看准备是否就绪。
121 |
122 | > flutter packages pub publish --dry-run
123 |
124 | 检查无误后,可以执行下面的命令,发布到[Pub](https://pub.dartlang.org/)上。
125 |
126 | > flutter packages pub publish
127 |
128 | ## 如何引用
129 |
130 | 对插件的引用有两种,已经发布的和未发布的。
131 |
132 | ### 引用发布的库
133 |
134 | flutter项目的很多资源管理都在根目录的pubspec.yaml下面,类似于js中的一些包管理一样,在`dependencies`加上我们需要引入的库,例如引入url_launcher库:
135 |
136 | ```
137 | dependencies:
138 | url_launcher: ^0.4.2
139 | ```
140 |
141 |
142 | 如果这个库包含了一些平台相关的东西,例如需要在native层进行使用的话,则需要在对应的native项目单独做引用。
143 |
144 | #### Android
145 |
146 | 修改android/build.gradle的dependencies处做引用:
147 |
148 | ```
149 | dependencies {
150 | provided rootProject.findProject(":url_launcher")
151 | }
152 | ```
153 |
154 |
155 | #### iOS
156 |
157 | 修改ios/hello.podspec文件
158 |
159 | ```
160 | Pod::Spec.new do |s|
161 | # lines skipped
162 | s.dependency 'url_launcher'
163 | ```
164 |
165 |
166 | #### 引用冲突
167 |
168 | 引用不同的库可能会导致一些冲突,例如A和B两个插件,都包含了C插件,但是所需的版本不同。因此我们可以采取以下措施避免这种问题:
169 |
170 | * 尽量使用范围版本而不是指定一个特定的版本。
171 | * 强制统一冲突的插件版本
172 | * 对于native层,android可以通过force命令强制指定版本,而iOS这边,Cocoapods则不支持引用的override功能。
173 |
174 | ### 引用未发布的库
175 |
176 | 引用未发布的库有两种方式,通过本地路径和git地址的方式:
177 |
178 | #### 基于Path的引用方式:
179 |
180 | 这种方式主要针对本地的未发布的库,引用的路径可以是相对或者绝对路径。
181 |
182 | ```
183 | dependencies:
184 | plugin1:
185 | path: ../plugin1/
186 | ```
187 |
188 |
189 | #### 基于Git的引用方式:
190 |
191 | 这种方式针对存放在git上的库,其中path是可选的,可以定位到某个子目录
192 | ```
193 | dependencies:
194 | package1:
195 | git:
196 | url: git://github.com/flutter/packages.git
197 | path: packages/package1
198 | ```
199 |
200 |
201 | ## 参考
202 |
203 | 1. [Flutter进阶—平台插件](https://blog.csdn.net/hekaiyou/article/details/72862653)
204 | 2. [Flutter - Creating a Plugin](https://www.youtube.com/watch?v=tErY3QWTZSA&t=883s)
205 | 3. [Flutter for Android Developers](https://flutter.io/flutter-for-android/)
206 | 4. [Writing custom platform-specific code with platform channels](https://flutter.io/platform-channels/)
207 | 5. [Developing Packages & Plugins](https://flutter.io/developing-packages/#step-2b-add-android-platform-code-javakt)
208 | 6. [Using Packages](https://flutter.io/using-packages/)
209 |
--------------------------------------------------------------------------------
/post/3. Flutter 布局详解.md:
--------------------------------------------------------------------------------
1 | # Flutter 布局详解
2 |
3 | > 本文主要介绍了Flutter布局相关的内容,对相关知识点进行了梳理,并从实际例子触发,进一步讲解该如何去进行布局。
4 |
5 | ## 1. 简介
6 |
7 | 在介绍Flutter布局之前,我们得先了解Flutter中的一些布局相关的特性。
8 |
9 | ### 1.1 边界约束(box constraints)
10 |
11 | box constraints有人也翻译为盒约束、箱约束,我个人还是觉得边界约束可能更直观一些。
12 |
13 | Flutter中的边界约束,是指widget可以按照指定限定条件,来决定自身如何占用布局空间。Flutter借鉴了很多React相关的东西,包括一些布局思想,但是它自身没有抽离出布局样式,而是用不同的widget去实现不同的布局,将样式嵌入widget中,用户可以像搭积木一样写布局,写法上跟React很像,只不过没了样式的设定。
14 |
15 | 这样做的好处,我觉得可能是为了统一的渲染。加入样式,会让布局复杂不少,在渲染层面会降低很多性能。因此,Flutter在大的方向上,加入不同类型的布局widget。在小的方向上,只给出很少的定制化的东西,将布局限定在有限的范围内,在完成布局的同时,让整个渲染能够统一,加快了更新和渲染。
16 |
17 | 但是,缺点也是同样明显,少了很多灵活性,不同的布局方式都被抽离出了widget,大家需要了解的widget非常多,增加了学习成本。
18 |
19 | ### 1.2 约束种类
20 |
21 | 在Flutter中,widget是由其底层的RenderBox渲染,渲染边界的约束(Constraints)由父级给出,widget在这些约束下调整自身尺寸。约束包括最小最大宽高,尺寸则是具体的宽高。
22 |
23 | 在Android中,布局的宽高限定有三种,match_parent、wrap_content以及具体尺寸。在Flutter中,也有这三种约束。
24 |
25 | * 尽可能大的约束,例如Center、ListView等;
26 | * 跟随内容大小的约束,例如Transform、Opacity等;
27 | * 指定尺寸的约束,例如Image、Text等;
28 |
29 | 但是,Flutter并没有把widget处理的这么绝对,这些约束条件包含在widget里,不像Android可以在外面去指定。因此,一些widget,例如Container,会根据参数的不同,约束条件也不同。Container默认是尽可能大的,但是给定尺寸的话,就会优先使用具体值。不同的widget可能设置条件不同、其子widget不同,约束条件也会不一样。Flutter将每种widget限制在不同的约束范围里,实际布局的时候,还需要综合去考虑。
30 |
31 | ## 2. 分类
32 |
33 | 按照约束条件来分类,很多widget不太好区分开来,官方也是根据其子元素的个数来分类。
34 |
35 | * 单个子元素(child)的布局,包括Container、Padding等`18`种(目前是2018年5月25日,后续我想肯定会增加的,下面类似);
36 | * 多个子元素(children)的布局,包括Row、Column等`11`种;
37 | * layout helper,例如ListView.Builder,在元素多的时候,用这种方式更加的高效,类似Android的RecyclerView,有自动的回收机制。这种严格意义上不能算是一个种类,我觉得这种helper会越来越多。
38 |
39 | ### 2.1 优缺点
40 |
41 | 其中日常中用的多的,例如Container、Padding、Center、Align、Row、Column、Stack、ListView等也有上十种。
42 |
43 | Flutter提供接近30多种不同的布局widget,还是源于其对widget的定位(在此处不再阐述,想了解的,可以翻看笔者之前文章的介绍)。对比其他移动端的开发平台,可以看出Flutter的布局widget是巨多,这也是为什么Flutter现在学习曲线很长的一个原因。
44 |
45 | 先来说下优点,统一渲染,更新效率更高。但是,对于普通开发者而言,是不会去太在乎这些的。性能高本来就是平台应该提供的最基本的能力,难道不是你应该提供的吗?
46 |
47 | 然后说下缺点吧,掌握起来还是非常费事的,布局起来也是挺蛋疼的。常规的布局还好,一到复杂的布局,觉得这个也能实现,那个也能实现,最后不知道哪个可以实现。
48 |
49 | ### 2.2 个人看法
50 |
51 | Flutter对于性能的优化,把平台侧的一些成本转接到开发者身上,不过呢,现在也是Flutter的初期,可以看出,整体的设计思路还是非常好的,也只有谷歌这种轮子大厂才敢这么干。但是,很明显少了些人为关怀,不同widget间约束条件穿插着,也可以说Flutter布局控件种类的增加,是其不断的打补丁导致的,后续的各种helper,也是为了填坑,这一块儿Flutter显然没有处理的很好。
52 |
53 | 但是,凡事都有个过程,不能说Flutter这些地方做的不好,只是目前看起来比较混乱,理想的架构设计,落地下来,可能就不是那么简单,开发者的需求千差万别,有了生态,什么都好说,当然这个过程,预计是会非常的缓慢。
54 |
55 |
56 | ## 3. widget详解
57 |
58 | 在Flutter中,我们平时自定义的widget,一般都是继承自StatefulWidget或StatelessWidget(并不是只有这两种),这两种widget也是目前最常用的两种。如果一个控件自身状态不会去改变,创建了就直接显示,不会有色值、大小或者其他属性的变化,这种widget一般都是继承自StatelessWidget,常见的有Container、ScrollView等。如果一个控件需要动态的去改变或者相应一些状态,例如点击态、色值、内容区域等,那么一般都是继承自StatefulWidget,常见的有CheckBox、AppBar、TabBar等。其实单纯的从名字也可以看出这两种widget的区别,这两种widget都是继承自Widget类。
59 |
60 | ### 3.1 Widget类
61 |
62 | Widget类在Flutter中是非常重要的,继承自Widget类的有PreferredSizeWidget、ProxyWidget、RenderObjectWidget、StatefulWidget、StatelessWidget。我们日常使用的绝大部分widget都是继承自Widget类,
63 |
64 | 查看Widget类源码,内部实现非常简单,构造函数如下
65 |
66 | ```
67 | const Widget({ this.key });
68 | final Key key;
69 | ```
70 |
71 | 这个key的作用,注视上写的很清楚,是用来控制在widget树中替换widget的时候使用的。其中Key类是Widget、Element以及SemanticsNode的唯一标识符,继承自Key的还有LocalKey以及GlobalKey。
72 |
73 | ### 3.2 State
74 |
75 | 在说到StatefulWidget之前,先说下State。State的作用有两点:
76 |
77 | 1. 在widget构建的时候可以被同步读取;
78 | 2. 在widget的生命周期中可能会被改变。
79 |
80 | #### 3.2.1 State生命周期
81 |
82 | State的生命周期有四种状态:
83 |
84 | * created:当State对象被创建时候,State.initState方法会被调用;
85 | * initialized:当State对象被创建,但还没有准备构建时,State.didChangeDependencies在这个时候会被调用;
86 | * ready:State对象已经准备好了构建,State.dispose没有被调用的时候;
87 | * defunct:State.dispose被调用后,State对象不能够被构建。
88 |
89 | 
90 |
91 | 完整生命周期如下:
92 |
93 | * 创建一个State对象时,会调用StatefulWidget.createState;
94 | * 和一个BuildContext相关联,可以认为被加载了(mounted);
95 | * 调用initState;
96 | * 调用didChangeDependencies;
97 | * 经过上述步骤,State对象被完全的初始化了,调用build;
98 | * 如果有需要,会调用didUpdateWidget;
99 | * 如果处在开发模式,热加载会调用reassemble;
100 | * 如果它的子树(subtree)包含需要被移除的State对象,会调用deactivate;
101 | * 调用dispose,State对象以后都不会被构建;
102 | * 当调用了dispose,State对象处于未加载(unmounted),已经被dispose的State对象没有办法被重新加载(remount)。
103 |
104 | #### 3.2.2 setState
105 |
106 | State中比较重要的一个方法是`setState`,当修改状态时,widget会被更新。比方说点击CheckBox,会出现选中和非选中状态之间的切换,就是通过修改状态来达到的。
107 |
108 | 查看setState源码,在一些异常的情况下将会抛出异常:
109 |
110 | * 传入的为null;
111 | * 处在defunct阶段;
112 | * created阶段还没有被加载(mounted);
113 | * 参数返回一个Future对象。
114 |
115 | 检查完一系列异常后,最后调用代码如下:
116 |
117 | ```
118 | _element.markNeedsBuild();
119 | ```
120 |
121 | markNeedsBuild内部,则是通过标记element为dirty,在下一帧的时候重建(rebuild)。可以看出setState并不是立即生效,它只是将widget进行了标记,真正的rebuild操作,则是等到下一帧的时候才会去进行。
122 |
123 | ### 3.3 StatefulWidget和StatelessWidget
124 |
125 | StatefulWidget和StatelessWidget如下所示
126 |
127 | 
128 |
129 | 一个StatelessWidget可以用多个不同的BuildContext构建,而一个StatefulWidget会为每个BuildContext创建一个State对象。
130 |
131 | #### 3.3.1 StatelessWidget
132 |
133 | 对于StatelessWidget,build方法会在如下三种情况下调用,
134 |
135 | 1. widget第一次被插入到树中;
136 | 2. widget的父节点更改了配置(configuration);
137 | 3. widget依赖的InheritedWidget改变了。
138 |
139 |
140 | ```
141 | class GreenFrog extends StatelessWidget {
142 | const GreenFrog({ Key key }) : super(key: key);
143 |
144 | @override
145 | Widget build(BuildContext context) {
146 | return new Container(color: const Color(0xFF2DBD3A));
147 | }
148 | }
149 | ```
150 |
151 | #### 3.3.2 StatefulWidget
152 |
153 | StatefulWidget的两个主要类别:
154 |
155 | 1. 在initState中创建资源,在dispose中销毁,但是不依赖于InheritedWidget或者调用setState方法,这类widget基本上用在一个应用或者页面的root;
156 | 2. 使用setState或者依赖于InheritedWidget,这种在营业生命周期中会被重建(rebuild)很多次。
157 |
158 | ```
159 | class YellowBird extends StatefulWidget {
160 | const YellowBird({ Key key }) : super(key: key);
161 |
162 | @override
163 | _YellowBirdState createState() => new _YellowBirdState();
164 | }
165 |
166 | class _YellowBirdState extends State {
167 | @override
168 | Widget build(BuildContext context) {
169 | return new Container(color: const Color(0xFFFFE306));
170 | }
171 | }
172 | ```
173 |
174 | ## 4. 如何布局
175 |
176 | 每个页面设计都不一样,相同页面可选择的布局方式也不一样,如果单纯的说应该如何去布局,我觉得不现实,大家可以参考下[Flutter官方的布局教程](https://flutterchina.club/tutorials/layout/#common-layout-widgets)。接下来,笔者,通过一个简单的页面,来一步一步的拆解布局的流程。整个过程,基本上按照拆解、组件封装、具体布局这三步来的。
177 |
178 | ### 4.1 拆解
179 |
180 | 
181 |
182 | #### 4.1.1 整体拆解
183 |
184 | 根据设计图,可以看出整体时分行展示的,因此最外层是一个Column元素
185 |
186 | * 第一行为标题,涉及到不对称的布局,可以用一个Stack或者Row来进行,用Row的话,则需要右边填上一个空白的widget占位。也可能会使用AppBar,将底部阴影去掉也能实现相同效果;
187 | * 第二行可以看作一个Row,分两块布局。右边部分,涉及到叠加,会考虑Stack;
188 | * 第三行比较复杂,整体看,也是一行一行进行展示的,因此最外层时一个Column。中间的文本部分需要根据个数自动换行,因此考虑使用Wrap。预习这个地方涉及到叠加,考虑Stack实现;
189 | * 第四行可以看作一个Row,分三块进行布局;
190 | * 第五行可以看作一个Row,分两块布局。
191 |
192 | 每一行之间的间隔,则可以考虑用Padding或者Container来设置。
193 |
194 | 通过上面这样一步一步的分析后,基本上对大致的布局有了一个了解,最外层的控件大致选对(只要能实现的话,就是复杂度以及效率的问题),然后一步一步的拆解每一行的元素,如果有重复的或者觉得可以封装出来的部分,则进行下一步。
195 |
196 | #### 4.1.2 局部拆解
197 |
198 | 每一行的拆解,大致也是按照这个思路来进行,因此笔者在这里就不做讲解了。
199 |
200 | ### 4.2 组件封装
201 |
202 | 例如上面,笔者想对第四行的这种展示进行封装,觉得今后的布局可能会用到,因此在这一步,可以先把这一块儿抽离出一个控件。利用Row的mainAxisAlignment以及Expanded来实现这种效果,具体的实现笔者不再详细的描述了。
203 |
204 | 经过这一步,整体的规划设计图已经有了,各个组件也都有了,接下来的工作就是组装了。
205 |
206 | ### 4.3 具体布局
207 |
208 | 具体布局设计到一些细节的地方,例如间隔(Padding或者Container)、居左居右居中(Align)、点击事件(GestureDetector)以及圆角(ClipRRect)等一些特殊情况,基本上就是嵌套,一层一层去实现。
209 |
210 | 在实际布局中,笔者实际使用的是Scaffold,顶部的AppBar将阴影直接去掉即可实现效果,body部分则实现2-5行的内容。最外层套一个Column也能实现,本质上都没什么区别,运行效果图如下所示。
211 |
212 | 
213 |
214 | ### 4.4 代码
215 |
216 | [代码Github地址](https://github.com/yang7229693/flutter-study)
217 |
218 | ## 5. 后话
219 |
220 | 笔者建了一个flutter学习相关的项目,[github地址](https://github.com/yang7229693/flutter-study),里面包含了笔者写的关于flutter学习相关的一些文章,会定期更新,也会上传一些学习demo,欢迎大家关注。
221 |
222 | ## 6. 参考
223 |
224 | 1. [Layout Widgets](https://flutter.io/widgets/layout/)
225 | 2. [Dealing with box constraints in Flutter](https://flutter.io/layout/)
226 | 3. [Flutter样式和布局控件简析(一)](https://segmentfault.com/a/1190000011949751)
227 | 4. [widgets library](https://docs.flutter.io/flutter/widgets/widgets-library.html)
228 | 5. [在Flutter中构建布局](https://flutterchina.club/tutorials/layout/#common-layout-widgets)
--------------------------------------------------------------------------------
/post/4. Flutter 布局(一)- Container详解.md:
--------------------------------------------------------------------------------
1 | # Flutter 布局(一)- Container详解
2 |
3 | > 本文主要介绍Flutter中非常常见的Container,列举了一些实际例子介绍如何使用。
4 |
5 | ## 1. 简介
6 |
7 | > A convenience widget that combines common painting, positioning, and sizing widgets.
8 |
9 | Container在Flutter中太常见了。官方给出的简介,是一个结合了绘制(painting)、定位(positioning)以及尺寸(sizing)widget的widget。
10 |
11 | 可以得出几个信息,它是一个组合的widget,内部有绘制widget、定位widget、尺寸widget。后续看到的不少widget,都是通过一些更基础的widget组合而成的。
12 |
13 | ### 1.1 组成
14 |
15 | Container的组成如下:
16 |
17 | * 最里层的是child元素;
18 | * child元素首先会被padding包着;
19 | * 然后添加额外的constraints限制;
20 | * 最后添加margin。
21 |
22 | Container的绘制的过程如下:
23 |
24 | * 首先会绘制transform效果;
25 | * 接着绘制decoration;
26 | * 然后绘制child;
27 | * 最后绘制foregroundDecoration。
28 |
29 | Container自身尺寸的调节分两种情况:
30 |
31 | * Container在没有子节点(children)的时候,会试图去变得足够大。除非constraints是unbounded限制,在这种情况下,Container会试图去变得足够小。
32 | * 带子节点的Container,会根据子节点尺寸调节自身尺寸,但是Container构造器中如果包含了width、height以及constraints,则会按照构造器中的参数来进行尺寸的调节。
33 |
34 | ### 1.2 布局行为
35 |
36 | 由于Container组合了一系列的widget,这些widget都有自己的布局行为,因此Container的布局行为有时候是比较复杂的。
37 |
38 | 一般情况下,Container会遵循如下顺序去尝试布局:
39 |
40 | * 对齐(alignment);
41 | * 调节自身尺寸适合子节点;
42 | * 采用width、height以及constraints布局;
43 | * 扩展自身去适应父节点;
44 | * 调节自身到足够小。
45 |
46 | 进一步说:
47 |
48 | * 如果没有子节点、没有设置width、height以及constraints,并且父节点没有设置unbounded的限制,Container会将自身调整到足够小。
49 | * 如果没有子节点、对齐方式(alignment),但是提供了width、height或者constraints,那么Container会根据自身以及父节点的限制,将自身调节到足够小。
50 | * 如果没有子节点、width、height、constraints以及alignment,但是父节点提供了bounded限制,那么Container会按照父节点的限制,将自身调整到足够大。
51 | * 如果有alignment,父节点提供了unbounded限制,那么Container将会调节自身尺寸来包住child;
52 | * 如果有alignment,并且父节点提供了bounded限制,那么Container会将自身调整的足够大(在父节点的范围内),然后将child根据alignment调整位置;
53 | * 含有child,但是没有width、height、constraints以及alignment,Container会将父节点的constraints传递给child,并且根据child调整自身。
54 |
55 | 另外,margin以及padding属性也会影响到布局。
56 |
57 | ### 1.3 继承关系
58 |
59 | ```
60 | Object > Diagnosticable > DiagnosticableTree > Widget > StatelessWidget > Container
61 | ```
62 |
63 | 从继承关系可以看出,Container是一个StatelessWidget。Container并不是一个最基础的widget,它是由一系列的基础widget组合而成。
64 |
65 | ## 2. 源码解析
66 |
67 | 构造函数如下:
68 |
69 | ```
70 | Container({
71 | Key key,
72 | this.alignment,
73 | this.padding,
74 | Color color,
75 | Decoration decoration,
76 | this.foregroundDecoration,
77 | double width,
78 | double height,
79 | BoxConstraints constraints,
80 | this.margin,
81 | this.transform,
82 | this.child,
83 | })
84 | ```
85 |
86 | 平时使用最多的,也就是padding、color、width、height、margin属性。
87 |
88 | ### 2.1 属性解析
89 |
90 | **key**:Container唯一标识符,用于查找更新。
91 |
92 | **alignment**:控制child的对齐方式,如果container或者container父节点尺寸大于child的尺寸,这个属性设置会起作用,有很多种对齐方式。
93 |
94 | **padding**:decoration内部的空白区域,如果有child的话,child位于padding内部。padding与margin的不同之处在于,padding是包含在content内,而margin则是外部边界,设置点击事件的话,padding区域会响应,而margin区域不会响应。
95 |
96 | **color**:用来设置container背景色,如果foregroundDecoration设置的话,可能会遮盖color效果。
97 |
98 | **decoration**:绘制在child后面的装饰,设置了decoration的话,就不能设置color属性,否则会报错,此时应该在decoration中进行颜色的设置。
99 |
100 | **foregroundDecoration**:绘制在child前面的装饰。
101 |
102 | **width**:container的宽度,设置为double.infinity可以强制在宽度上撑满,不设置,则根据child和父节点两者一起布局。
103 |
104 | **height**:container的高度,设置为double.infinity可以强制在高度上撑满。
105 |
106 | **constraints**:添加到child上额外的约束条件。
107 |
108 | **margin**:围绕在decoration和child之外的空白区域,不属于内容区域。
109 |
110 | **transform**:设置container的变换矩阵,类型为Matrix4。
111 |
112 | **child**:container中的内容widget。
113 |
114 | ### 2.2 一个例子
115 |
116 | ```
117 | new Container(
118 | constraints: new BoxConstraints.expand(
119 | height:Theme.of(context).textTheme.display1.fontSize * 1.1 + 200.0,
120 | ),
121 | decoration: new BoxDecoration(
122 | border: new Border.all(width: 2.0, color: Colors.red),
123 | color: Colors.grey,
124 | borderRadius: new BorderRadius.all(new Radius.circular(20.0)),
125 | image: new DecorationImage(
126 | image: new NetworkImage('http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f06c.jpg'),
127 | centerSlice: new Rect.fromLTRB(270.0, 180.0, 1360.0, 730.0),
128 | ),
129 | ),
130 | padding: const EdgeInsets.all(8.0),
131 | alignment: Alignment.center,
132 | child: new Text('Hello World',
133 | style: Theme.of(context).textTheme.display1.copyWith(color: Colors.black)),
134 | transform: new Matrix4.rotationZ(0.3),
135 | )
136 | ```
137 |
138 | 这是官方文档给出例子的一个变种,包含属性比较全,可以看下其用法。实际运行效果如下:
139 |
140 | 
141 |
142 | 其中decoration可以设置边框、背景色、背景图片、圆角等属性,非常实用。对于transform这个属性,一般有过其他平台开发经验的,都大致了解,这种变换,一般不是变换的实际位置,而是变换的绘制效果,也就是说它的点击以及尺寸、间距等都是按照未变换前的。
143 |
144 | ### 2.3 源码
145 |
146 | ```
147 | decoration = decoration ?? (color != null ? new BoxDecoration(color: color) : null),
148 | ```
149 | 可以看出,对于颜色的设置,最后都是转换为decoration来进行绘制的。如果同时包含decoration和color两种属性,则会报错。
150 |
151 | ```
152 | @override
153 | Widget build(BuildContext context) {
154 | Widget current = child;
155 |
156 | if (child == null && (constraints == null || !constraints.isTight)) {
157 | current = new LimitedBox(
158 | maxWidth: 0.0,
159 | maxHeight: 0.0,
160 | child: new ConstrainedBox(constraints: const BoxConstraints.expand())
161 | );
162 | }
163 |
164 | if (alignment != null)
165 | current = new Align(alignment: alignment, child: current);
166 |
167 | final EdgeInsetsGeometry effectivePadding = _paddingIncludingDecoration;
168 | if (effectivePadding != null)
169 | current = new Padding(padding: effectivePadding, child: current);
170 |
171 | if (decoration != null)
172 | current = new DecoratedBox(decoration: decoration, child: current);
173 |
174 | if (foregroundDecoration != null) {
175 | current = new DecoratedBox(
176 | decoration: foregroundDecoration,
177 | position: DecorationPosition.foreground,
178 | child: current
179 | );
180 | }
181 |
182 | if (constraints != null)
183 | current = new ConstrainedBox(constraints: constraints, child: current);
184 |
185 | if (margin != null)
186 | current = new Padding(padding: margin, child: current);
187 |
188 | if (transform != null)
189 | current = new Transform(transform: transform, child: current);
190 |
191 | return current;
192 | }
193 | ```
194 | Container的build函数不长,绘制也是一个线性的判断的过程,一层一层的包裹着widget,去实现不同的样式。
195 |
196 | 最里层的是child,如果为空或者其他约束条件,则最里层包含的为一个LimitedBox,然后依次是Align、Padding、DecoratedBox、前景DecoratedBox、ConstrainedBox、Padding(实现margin效果)、Transform。
197 |
198 | Container的源码本身并不复杂,复杂的是它的各种布局表现。我们谨记住一点,如果内部不设置约束,则按照父节点尽可能的扩大,如果内部有约束,则按照内部来。
199 |
200 | ### 2.4 使用场景
201 |
202 | Container算是目前项目中,最经常用到的一个widget。在实际使用过程中,笔者在以下情况会使用到Container,当然并不是绝对的,也可以通过其他widget来实现。
203 |
204 | * 需要设置间隔(这种情况下,如果只是单纯的间隔,也可以通过Padding来实现);
205 | * 需要设置背景色;
206 | * 需要设置圆角或者边框的时候(ClipRRect也可以实现圆角效果);
207 | * 需要对齐(Align也可以实现);
208 | * 需要设置背景图片的时候(也可以使用Stack实现)。
209 |
210 | ## 3. 例子
211 |
212 | 接下来我们试着去做一个圆角按钮,它包含以下特性:
213 |
214 | * 支持设置按钮的三种状态(正常态、点击态、禁用态)的色值;
215 | * 支持设置按钮标题;
216 | * 支持设置宽高;
217 | * 支持点击回调;
218 |
219 | 根据上面介绍,利用decoration这个属性,基本上就可以完成效果了,至于点击效果以及点击回调,则使用一个GestureDetector就可以完成了。实际的例子非常简单,在这里就不贴代码了。实际运行效果如下所示:
220 |
221 | 
222 |
223 | ### 3.1 注意事项
224 |
225 | 这个小控件,写起来很简单,本身没有什么难度,只是纯粹的介绍了Container的使用方法,但是有一个地方需要注意的。在控件的deactivate状态,我们需要将控件的属性初始到最开始的状态,例如在本例中,有如下代码:
226 |
227 | ```
228 | @override
229 | void deactivate() {
230 | super.deactivate();
231 | currentColor = widget.backgroundColor;
232 | }
233 | ```
234 |
235 | 这么做是为什么了?是因为在点击按钮进行页面跳转的时候,按钮处在点击态,当我们返回的时候,页面还是处在点击态,这显然就不正确了,因此需要我们手动的在deactivate状态下,将控件恢复到初始状态。但是呢,这个设置颜色,并不是说在deactivate的时候,就立马去刷新控件,而是在下次再进入这个页面的时候,再次运行build的时候,会按照这个初始值进行绘制,也就是恢复到了最开始的状态。
236 |
237 | ### 3.2 代码
238 |
239 | [代码Github地址](https://github.com/yang7229693/flutter-study),这是一个系列的项目,如果不出意外,会将Flutter中常见的二十多种布局widget都介绍一下。
240 |
241 | ## 4. 后话
242 |
243 | 笔者建了一个flutter学习相关的项目,[github地址](https://github.com/yang7229693/flutter-study),里面包含了笔者写的关于flutter学习相关的一些文章,会定期更新,也会上传一些学习demo,欢迎大家关注。
244 |
245 |
246 | ## 5. 参考
247 |
248 | 1. [Container class](https://docs.flutter.io/flutter/widgets/Container-class.html)
--------------------------------------------------------------------------------
/post/5. Flutter 布局(二)- Padding、Align、Center详解.md:
--------------------------------------------------------------------------------
1 | # Flutter 布局(二)- Padding、Align、Center详解
2 |
3 | > 本文主要介绍Flutter布局中的Padding、Align以及Center控件,详细介绍了其布局行为以及使用场景,并对源码进行了分析。
4 |
5 | ## 1. Padding
6 |
7 | > A widget that insets its child by the given padding.
8 |
9 | ### 1.1 简介
10 |
11 | Padding在Flutter中用的也挺多的,作为一个基础的控件,功能非常单一,给子节点设置padding属性。写过其他端的都了解这个属性,就是设置内边距属性,内边距的空白区域,也是widget的一部分。
12 |
13 | Flutter中并没有单独的Margin控件,在Container中有margin属性,看源码关于margin的实现。
14 |
15 | ```
16 | if (margin != null)
17 | current = new Padding(padding: margin, child: current);
18 | ```
19 |
20 | 不难看出,Flutter中淡化了margin以及padding的区别,margin实质上也是由Padding实现的。
21 |
22 | ### 1.2 布局行为
23 |
24 | Padding的布局分为两种情况:
25 |
26 | * 当child为空的时候,会产生一个宽为left+right,高为top+bottom的区域;
27 | * 当child不为空的时候,Padding会将布局约束传递给child,根据设置的padding属性,缩小child的布局尺寸。然后Padding将自己调整到child设置了padding属性的尺寸,在child周围创建空白区域。
28 |
29 | ### 1.3 继承关系
30 |
31 | ```
32 | Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Padding
33 | ```
34 |
35 | 从继承关系可以看出,Padding控件是一个基础控件,不像Container这种组合控件。Container中的margin以及padding属性都是利用Padding控件去实现的。
36 |
37 | #### 1.3.1 关于SingleChildRenderObjectWidget
38 |
39 | SingleChildRenderObjectWidget是RenderObjectWidgets的一个子类,用于限制只能有一个子节点。它只提供child的存储,而不提供实际的更新逻辑。
40 |
41 | #### 1.3.2 关于RenderObjectWidgets
42 |
43 | RenderObjectWidgets为RenderObjectElement提供配置,而RenderObjectElement包含着(wrap)RenderObject,RenderObject则是在应用中提供实际的绘制(rendering)的元素。
44 |
45 | ### 1.4 示例代码
46 |
47 | 实例代码直接上官方的例子,非常的简单:
48 |
49 | ```
50 | new Padding(
51 | padding: new EdgeInsets.all(8.0),
52 | child: const Card(child: const Text('Hello World!')),
53 | )
54 | ```
55 | 例子中对Card设置了一个宽度为8的内边距。
56 |
57 | ### 1.5 源码解析
58 |
59 | 构造函数如下:
60 |
61 | ```
62 | const Padding({
63 | Key key,
64 | @required this.padding,
65 | Widget child,
66 | })
67 | ```
68 | 包含一个padding属性,相当的简单。
69 |
70 | #### 1.5.1 属性解析
71 |
72 | **padding**:padding的类型为`EdgeInsetsGeometry`,EdgeInsetsGeometry是EdgeInsets以及EdgeInsetsDirectional的基类。在实际使用中不涉及到国际化,例如适配阿拉伯地区等,一般都是使用EdgeInsets。EdgeInsetsDirectional光看命名就知道跟方向相关,因此它的四个边距不限定上下左右,而是根据方向来定的。
73 |
74 | #### 1.5.2 源码
75 |
76 | ```
77 | @override
78 | RenderPadding createRenderObject(BuildContext context) {
79 | return new RenderPadding(
80 | padding: padding,
81 | textDirection: Directionality.of(context),
82 | );
83 | }
84 | ```
85 |
86 | Padding的创建函数,实际上是由`RenderPadding`来进行的。
87 |
88 | 关于RenderPadding的实际布局表现,当child为null的时候:
89 |
90 | ```
91 | if (child == null) {
92 | size = constraints.constrain(new Size(
93 | _resolvedPadding.left + _resolvedPadding.right,
94 | _resolvedPadding.top + _resolvedPadding.bottom
95 | ));
96 | return;
97 | }
98 | ```
99 |
100 | 返回一个宽为_resolvedPadding.left+_resolvedPadding.right,高为_resolvedPadding.top+_resolvedPadding.bottom的区域。
101 |
102 | 当child不为null的时候,经历了三个过程,即调整child尺寸、调整child位置以及调整Padding尺寸,最终达到实际的布局效果。
103 |
104 | ```
105 | // 调整child尺寸
106 | final BoxConstraints innerConstraints = constraints.deflate(_resolvedPadding);
107 | child.layout(innerConstraints, parentUsesSize: true);
108 |
109 | // 调整child位置
110 | final BoxParentData childParentData = child.parentData;
111 | childParentData.offset = new Offset(_resolvedPadding.left, _resolvedPadding.top);
112 |
113 | // 调整Padding尺寸
114 | size = constraints.constrain(new Size(
115 | _resolvedPadding.left + child.size.width + _resolvedPadding.right,
116 | _resolvedPadding.top + child.size.height + _resolvedPadding.bottom
117 | ));
118 | ```
119 |
120 | 到此处,上面介绍的padding布局行为就解释的通了。
121 |
122 | ### 1.6 使用场景
123 |
124 | Padding本身还是挺简单的,基本上需要间距的地方,它都能够使用。如果在单一的间距场景,使用Padding比Container的成本要小一些,毕竟Container里面包含了多个widget。Padding能够实现的,Container都能够实现,只不过,Container更加的复杂。
125 |
126 | ## 2. Align
127 |
128 | > A widget that aligns its child within itself and optionally sizes itself based on the child's size.
129 |
130 | ### 2.1 简介
131 |
132 | 在其他端的开发,Align一般都是当做一个控件的属性,并没有拿出来当做一个单独的控件。Align本身实现的功能并不复杂,设置child的对齐方式,例如居中、居左居右等,并根据child尺寸调节自身尺寸。
133 |
134 | ### 2.2 布局行为
135 |
136 | Align的布局行为分为两种情况:
137 |
138 | * 当widthFactor和heightFactor为null的时候,当其有限制条件的时候,Align会根据限制条件尽量的扩展自己的尺寸,当没有限制条件的时候,会调整到child的尺寸;
139 | * 当widthFactor或者heightFactor不为null的时候,Aligin会根据factor属性,扩展自己的尺寸,例如设置widthFactor为2.0的时候,那么,Align的宽度将会是child的两倍。
140 |
141 | Align为什么会有这样的布局行为呢?原因很简单,设置对齐方式的话,如果外层元素尺寸不确定的话,内部的对齐就无法确定。因此,会有宽高因子、根据外层限制扩大到最大尺寸、外层不确定时调整到child尺寸这些行为。
142 |
143 | ### 2.3 继承关系
144 |
145 | ```
146 | Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Align
147 | ```
148 |
149 | 可以看出,Align跟Padding一样,也是一个非常基础的组件,Container中的align属性,也是使用Align去实现的。
150 |
151 | ### 2.4 示例代码
152 |
153 | ```
154 | new Align(
155 | alignment: Alignment.center,
156 | widthFactor: 2.0,
157 | heightFactor: 2.0,
158 | child: new Text("Align"),
159 | )
160 | ```
161 |
162 | 例子依旧很简单,设置一个宽高为child两倍区域的Align,其child处在正中间。
163 |
164 | ### 2.5 源码解析
165 |
166 | ```
167 | const Align({
168 | Key key,
169 | this.alignment: Alignment.center,
170 | this.widthFactor,
171 | this.heightFactor,
172 | Widget child
173 | })
174 | ```
175 |
176 | Align的构造函数基本上就是宽高因子、对齐方式属性。日常使用中,宽高因子属性基本上用的不多。如果是复杂的布局,Container内部的align属性也可以实现相同的效果。
177 |
178 | #### 2.5.1 属性解析
179 |
180 | ***alignment***:对齐方式,一般会使用系统默认提供的9种方式,但是并不是说只有这9种,例如如下的定义。系统提供的9种方式只是预先定义好的。
181 |
182 | ```
183 | /// The top left corner.
184 | static const Alignment topLeft = const Alignment(-1.0, -1.0);
185 | ```
186 |
187 | Alignment实际上是包含了两个属性的,其中第一个参数,-1.0是左边对齐,1.0是右边对齐,第二个参数,-1.0是顶部对齐,1.0是底部对齐。根据这个规则,我们也可以自定义我们需要的对齐方式,例如
188 |
189 | ```
190 | /// 居右高于底部1/4处.
191 | static const Alignment rightHalfBottom = alignment: const Alignment(1.0, 0.5),
192 | ```
193 |
194 | ***widthFactor***:宽度因子,如果设置的话,Align的宽度就是child的宽度乘以这个值,不能为负数。
195 |
196 | ***heightFactor***:高度因子,如果设置的话,Align的高度就是child的高度乘以这个值,不能为负数。
197 |
198 | #### 2.5.2 源码
199 |
200 | ```
201 | @override
202 | RenderPositionedBox createRenderObject(BuildContext context) {
203 | return new RenderPositionedBox(
204 | alignment: alignment,
205 | widthFactor: widthFactor,
206 | heightFactor: heightFactor,
207 | textDirection: Directionality.of(context),
208 | );
209 | }
210 | ```
211 |
212 | Align的实际构造调用的是`RenderPositionedBox`。
213 |
214 | RenderPositionedBox的布局表现如下:
215 |
216 | ```
217 | // 根据_widthFactor、_heightFactor以及限制因素来确定宽高
218 | final bool shrinkWrapWidth = _widthFactor != null || constraints.maxWidth == double.infinity;
219 | final bool shrinkWrapHeight = _heightFactor != null || constraints.maxHeight == double.infinity;
220 |
221 | if (child != null) {
222 | // 如果child不为null,则根据规则设置Align的宽高,如果需要缩放,则根据_widthFactor是否为null来进行缩放,如果不需要,则尽量扩展。
223 | child.layout(constraints.loosen(), parentUsesSize: true);
224 | size = constraints.constrain(new Size(shrinkWrapWidth ? child.size.width * (_widthFactor ?? 1.0) : double.infinity,
225 | shrinkWrapHeight ? child.size.height * (_heightFactor ?? 1.0) : double.infinity));
226 | alignChild();
227 | } else {
228 | // 如果child为null,如果需要缩放,则变为0,否则就尽量扩展
229 | size = constraints.constrain(new Size(shrinkWrapWidth ? 0.0 : double.infinity,
230 | shrinkWrapHeight ? 0.0 : double.infinity));
231 | }
232 | ```
233 |
234 | ### 2.6 使用场景
235 |
236 | 一般在对齐场景下使用,例如需要右对齐或者底部对齐之类的。它能够实现的功能,Container都能实现。
237 |
238 | ## 3. Center
239 |
240 | Center继承自Align,只不过是将alignment设置为Alignment.center,其他属性例如widthFactor、heightFactor,布局行为,都与Align完全一样,在这里就不再单独做介绍了。Center源码如下,没有设置alignment属性,是因为Align默认的对齐方式就是居中。
241 |
242 | ```
243 | class Center extends Align {
244 | /// Creates a widget that centers its child.
245 | const Center({ Key key, double widthFactor, double heightFactor, Widget child })
246 | : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
247 | }
248 | ```
249 |
250 | ## 4. 后话
251 |
252 | 笔者建了一个flutter学习相关的项目,[github地址](https://github.com/yang7229693/flutter-study),里面包含了笔者写的关于flutter学习相关的一些文章,会定期更新,也会上传一些学习demo,欢迎大家关注。
253 |
254 | ## 5. 参考
255 |
256 | 1. [Padding class](https://docs.flutter.io/flutter/widgets/Padding-class.html)
257 | 2. [EdgeInsetsGeometry class](https://docs.flutter.io/flutter/painting/EdgeInsetsGeometry-class.html)
258 | 3. [EdgeInsets class](https://docs.flutter.io/flutter/painting/EdgeInsets-class.html)
259 | 4. [EdgeInsetsDirectional class](https://docs.flutter.io/flutter/painting/EdgeInsetsDirectional-class.html)
260 | 5. [RenderPadding class](https://docs.flutter.io/flutter/rendering/RenderPadding-class.html)
261 | 6. [Align class](https://docs.flutter.io/flutter/widgets/Align-class.html)
262 | 7. [Center class](https://docs.flutter.io/flutter/widgets/Center-class.html)
--------------------------------------------------------------------------------
/post/7. 在现有项目中添加Flutter.md:
--------------------------------------------------------------------------------
1 | # 现有项目中集成Flutter
2 |
3 | > 本文列举了项目开发使用Flutter会遇到的问题,以及如何使用Flutter module在现有项目中集成Flutter,并对其原理进行了分析。
4 |
5 | 最近在做的一个商业项目,完全的使用Flutter编写的,这其中的坑,只有写过的人才能体会到。
6 |
7 | ## 1. 纯Flutter项目的问题
8 |
9 | 在论述纯Flutter项目问题之前,我先表述下我的观点(仅限于纯Flutter项目,目前时间2018年6月26日,不排除Flutter的发展,让我的观点改观):
10 |
11 | * 对于个人开发者,可以使用纯Flutter去开发App尝鲜;
12 | * 对于小团队,不推荐使用纯Flutter,出了问题,解决不了,浪费时间;
13 | * 对于商业项目,不推荐使用纯Flutter,体验不好,埋坑时间不少。
14 | * 与硬件强相关的项目,不推荐使用纯Flutter。
15 |
16 | 对于使用Flutter的初衷,我相信大部分领导都是出于提高生产力。但是目前就我们的项目来看,相同的时间,如果改用Native去写,我觉得两者进度并没有多大的差异,可能Native端反而会更快一些。目前Flutter中非常常见的一些控件功能都无法满足,往往在轮子上需要耗费大量的时间,反而在业务层面花费的时间很少。
17 |
18 | ### 1.1 目前存在的一些问题
19 |
20 | * 适配问题:Flutter说的是跨平台,但是没有很完美的解决各个屏幕差异所带来的问题。实际上还是需要去做一些适配;
21 |
22 | * 性能问题:目前看这个问题特别突出,在一些性能低的Android手机上,会出现一些卡顿问题。在一些高端机型上,一些转场动画,效果也不是特别理想,一旦涉及到一些复杂的页面,切换页面就会出现很明显的卡顿问题;
23 |
24 | * 硬件相关问题:这个也是Flutter需要急需解决的问题,第三方硬件相关插件质量参差不齐,官方插件质量也堪忧。例如官方的camera插件,各种crash问题。
25 |
26 | * 生命周期问题:插件层对生命周期的监控,是App级别的,无法针对某一个页面。Flutter中控件也没有很明确的生命周期这一概念,就是两三种状态的切换,没有像React中的生命周期,更不用说像Native中的那样。
27 |
28 | 上面这些问题是在项目中实际遇到的,当然一些问题通过变换实现手段可以规避,一些轮子自己花些时间造。一个新技术的初期,尤其是这种跨平台技术,选择all in的,还是需要再三考量。
29 |
30 | ### 1.2 前景
31 |
32 | 前面说的一些问题,并不是说Flutter非常差劲。如果说生态非常成熟的Flutter,我会非常愿意去使用,这项技术目前看确实挺有吸引力的。抛开写着写着就感觉自己像个web开发之外,其实写起来并没有太多的负担。
33 |
34 | 移动端技术现在已经是处在一个非常成熟平稳的时期,所以跨平台技术才会如此的迫切,单纯的去召集两个team开发两个端,这种成本在目前来看确实比较高,尤其是一些日活较低的产品。
35 |
36 | 前段时间,炒得沸沸扬扬的Airbnb抛弃RN的新闻,让大家对RN以及跨平台技术产生了一些不确定。跨平台技术从来都是公司层面的需求,并不是程序员个人的需求。况且,任何技术都不能忽略平台背后的商业推动,我不是一个跨平台技术的追求者,我个人也一直觉得跨平台是个伪命题。
37 |
38 | 追求纯粹的跨平台,无疑是条死路,平台差异中追求共通点,这才是大出路。我想这也是为什么Flutter要去实现,在现有项目中集成Flutter的原因吧。
39 |
40 | ## 2. 现有的项目中使用Flutter
41 |
42 | 官方一直在努力让Flutter更好的接入现有的移动端(iOS/Android)项目中,这个目的不言而喻。如果这个弄不好,肯定不会有太多商业项目愿意去使用Flutter,就像RN一样。
43 |
44 | ### 2.1 Android端
45 |
46 | Android端方案目前稍微算是稳定一些,但是性能效率方面还是堪忧。因此本文主要偏重于介绍Android端目前来说算是相对稳定的一种方案,也就是采用Flutter module模板的方式。
47 |
48 | #### 2.1.1 切换Flutter分支
49 |
50 | 我们默认安装的Flutter版本是beta版本,目前(2018年6月29日)版本还没有支持在现有项目中集成Flutter module的模板功能。
51 |
52 | > flutter channel
53 |
54 | 一般的用户可以看到输出如下信息:
55 |
56 | ```
57 | Flutter channels:
58 | * beta
59 | dev
60 | master
61 | ```
62 |
63 | 因此,我们切换到master分支。
64 |
65 | > flutter channel master
66 |
67 | 然后运行更新命令
68 |
69 | > flutter upgrade
70 |
71 | #### 2.1.2 创建Flutter module模板
72 |
73 | 这个功能是在2018年6月22日发布在master分支的,目前也只是早期的preview版本。我们在一个Android项目目录同级目录下创建模板工程。
74 |
75 | > flutter create -t module flutter_module
76 |
77 | 创建的项目目录下面有两个隐藏文件夹,分别是.android和.ios。其中.android中包含后续我们需要使用的一些代码,例如封装好的Flutter以及FlutterFragment的Java代码。
78 |
79 | #### 2.1.3 添加Flutter module到Android项目中
80 |
81 | 修改Android项目根目录的settings.gradle,将Flutter module作为一个子工程添加到项目中
82 |
83 | ```
84 | include ':app' // assumed existing content
85 | setBinding(new Binding([gradle: this])) // new
86 | evaluate(new File( // new
87 | settingsDir.parentFile, // new
88 | 'flutter_module/.android/include_flutter.groovy' // new
89 | )) // new
90 | ```
91 |
92 | Sync一下,可以发现添加了两个module到项目中了。其中一个是flutter的module,其中包含了一些简单的封装,供Java代码调用。另一个是package_info的module,是一个Flutter插件,其代码非常简单,就是获取app名称、包名、版本等信息。
93 |
94 | 在app的build.gradle中添加依赖
95 |
96 | ```
97 | dependencies {
98 | implementation project(':flutter')
99 | ```
100 |
101 | Sync一下,不出意外的话,应该不会有什么错误,到此,这个Flutter module就被添加到了Android项目中了。
102 |
103 | #### 2.1.4 Java代码调用Flutter module
104 |
105 | 使用Flutter module中的Java API,添加一个Flutter view到页面上。
106 |
107 | ```
108 | val flutterView = Flutter.createView(
109 | this@MainActivity,
110 | lifecycle,
111 | "route1"
112 | )
113 |
114 | val layout = FrameLayout.LayoutParams(600, 800)
115 | layout.leftMargin = 100
116 | layout.topMargin = 200
117 | addContentView(flutterView, layout)
118 | ```
119 |
120 | 上面代码是添加到一个文本的点击事件中的,其中FlutterView可以看作是Flutter代码展示的容器。展示的宽600高800的部分,实际上是Flutter的代码生成的。其中的route1则是写在Flutter中的,生成了一个绿色背景的Container。代码如下
121 |
122 | ```
123 | case 'route1':
124 | return Container(
125 | child: Center(child: Text('Route 1\n${packageInfo.appName}')),
126 | color: Colors.green,
127 | );
128 | ```
129 |
130 | 在真机上运行,效果挺差劲的,点击了文本过后,会先黑一下屏,然后将这个FlutterView添加到页面上,整个过程也很缓慢,这样子肯定是没法在项目中使用。
131 |
132 | 
133 |
134 |
135 | 到此,已经完成了Android调用Flutter代码的全过程了,我们来梳理一下整个流程:
136 |
137 | 1. 切换Flutter分支到master,目前beta分支上没有包含模板工程;
138 | 2. 生成Flutter module工程;
139 | 3. 修改Android代码的配置,将Flutter module添加到Android项目中;
140 | 4. 在模板工程的lib下编写相关的Flutter代码,在Android中调用。
141 |
142 | ### 2.2 将Flutter项目转换为module
143 |
144 | 这个目前是在试验阶段,如果有愿意尝试的,也可以按照官方的例子去走一遍,不过大家最好也得有心理准备,官方文档上说会出现一系列问题,在此笔者不做进一步的尝试了。整个过程并不复杂,也是需要切换到master分支上去进行的。如果这种方案稳定下来,肯定会比上面的那种module方式更加的方便。
145 |
146 | [官方步骤传送门](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps#experiment-turn-the-flutter-project-into-a-module)
147 |
148 | ### 2.3 iOS端
149 |
150 | [官方步骤传送门](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps#experiment-integrate-flutterviewcontroller)
151 |
152 | 目前也是试验阶段,如果想要尝试的话,也需要切换到master分支上去进行的。
153 |
154 | ### 2.4 关于FlutterView
155 |
156 | FlutterView在插件层面比较常见,是Flutter层的一个Java API。实际上可以把它看是Android端的一个View,只不过里面包含的是Flutter的内容。例如将相机封装成一个Flutter控件,就需要借助FlutterView,将预览输出到FlutterView上。
157 |
158 | 在Native项目中集成Flutter,FlutterView也起到了很重要的作用。Flutter层内容的输出,也都是通过FlutterView来实现的。
159 |
160 | FlutterView继承自SurfaceView,它像是一个大杂烩,它包含了或者监听了尽可能多的事件,例如键盘、物理按键、生命周期、广播、Surface回调、横竖屏切换等等。基本上把Android端一个View可能存在的一些事件或者状态,都添加上去,让Flutter层能够得知尽可能多的状态和回调。
161 |
162 | FlutterView除去各种监听事件,内部实际的工作是由FlutterNativeView去实现的。其本质也是一个插件接口,只不过是Native调用Flutter层的,它们之间通过MethodChannel进行通信的。
163 |
164 | ### 2.5 原理
165 |
166 | 通过Flutter module中的flutter模块,我们可以看出其本质上还是通过MethodChannel进行调用的。这是Flutter官方提供的一种插件能力,并不是说只能单向调用,也可以在Native端调用Flutter。
167 |
168 | 但是呢,这个调用是异步的,目前看,Native端调用Flutter层效果并不是很理想。目前笔者也是在debug下进行测试的,release环境下应该会好一点吧。如果需要在Native项目中集成Flutter,则还需要进行优化,例如提前初始化等。
169 |
170 | ## 3. 其他方法
171 |
172 | 在Flutter module没有被放出之前,其他公司一般都是怎么去实现这种混编的呢。如上面所述,我觉得都是利用了FlutterView。如果我们不依赖Flutter module,在Native中引入Flutter库,直接使用FlutterView进行页面编写,这个本身也不是什么困难的事情。难就难在进行性能优化达到上线的条件。
173 |
174 | MethodChannel这种Natvive与Flutter之间的通信方式,给了这种混编的一种可能性。还是期待Flutter官方能把这种混编模式完善起来。
175 |
176 | 最后说一句,Flutter里面造起轮子来,简直就是太没人性了。
177 |
178 | ## 4. 后话
179 |
180 | 笔者建的一个Flutter学习相关的项目,[Github地址](https://github.com/yang7229693/flutter-study),里面包含了笔者写的关于Flutter学习相关的一些文章,会定期更新,也会上传一些学习demo,欢迎大家关注。
181 |
182 | ## 5. 参考
183 |
184 | 1. [Add Flutter to existing apps](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps)
185 | 2. [Upgrading Flutter](https://flutter.io/upgrading/)
186 |
--------------------------------------------------------------------------------