├── 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 | ![Flutter不同平台表现](http://whysodiao.com/images/Flutter-Platform-Show.png) 84 | 85 | Flutter对于移动开发人员,最诱惑的能力是其完全的跨平台特性,不同于RN这种一处学到处写,它是一处写到出跑,但是他跟其他的跨平台有何区别呢? 86 | 87 | ### 跨平台解决方案 88 | 89 | 市面上的跨平台解决方案,可以大致归结为两类: 90 | 91 | * 使用平台支持的web技术:这些解决方案基本上加载了应用程序中的移动浏览器,并在该浏览器中执行所有的逻辑,例如PhoneGap。 92 | * 本地跨平台:程序员编写的代码自动转换为Native代码,这种方式的优点是近乎原生的性能,例如RN、Weex、Xamarin等。 93 | 94 | 这些方案是否真正的解决了跨平台问题呢?从目前的状况来看,很显然是没有的,因为它们都始终逃不开性能、包大小、流畅性、内存、平台特性等问题。 95 | 96 | ![2017年8月跨平台性能测试](http://whysodiao.com/images/cross-platform-performance.png) 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 | ![Flutter](http://whysodiao.com/images/flutter-platform.png) 111 | 112 | Flutter将UI组件和渲染器从平台移动到应用程序中,这使得它们可以自定义和可扩展。Flutter唯一要求系统提供的是canvas,以便定制的UI组件可以出现在设备的屏幕上。 113 | 114 | ### Flutter框架 115 | 116 | Flutter框架是一个分层的结构,每个层都建立在前一层之上。 117 | 118 | ![Flutter 框架](http://whysodiao.com/images/flutter-architecture.png) 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 | ![众测人次及启动次数](http://whysodiao.com/images/Crash问题.png) 137 | 138 | 参与人次大概150人左右,启动次数大概500次左右,没有出现一次Crash数据上报,由于app很简单,并不能说明很多问题,但是众测用户反馈了约12条信息,其中1条是类似于ANR,无法操作,其余的部分则是卡顿相关的反馈。 139 | 140 | ### 流畅性 141 | 142 | 将官方的例子发给测试同学,让在iOS以及Android平台的不同机子上运行了下。在iOS上基本上流畅运行,没有出现卡顿的现象,在Android部分设备上,出现了卡顿的现象。 143 | 144 | ![Android流畅性评测](http://whysodiao.com/images/Android流畅性评测.png) 145 | 146 | ![iOS流畅性评测](http://whysodiao.com/images/iOS流畅性评测.png) 147 | 148 | 由于没有复杂的例子,其实这个流畅性的测试,意义不是特别大,官方简单的控件展示demo程序,本身就很简单,但是在Android上还是出现了不少问题,只能说明整体还有非常大的优化空间。 149 | 150 | ### 编写复杂程度 151 | 152 | 试着照着一张设计稿进行了简单的纯布局代码工作,初次接触用起来还是比较复杂,尤其是那恐怖的嵌套层级,对代码维护来说绝对是个问题,而且由于Flutter的widget机制,很多组件只支持最基本的操作,例如一些扩展的属性,都得自己去实现,况且现在组件库还不是非常的丰富。代码量也比较多,整个代码大概有500行左右,还只是不涉及到一些交互以及数据绑定等。 153 | 154 | ![iOS和Android运行对比](http://whysodiao.com/images/flutter-app-show.png) 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 | ![帧](http://whysodiao.com/images/frame.jpg) 28 | 29 | 帧里面又分为关键帧和过渡帧,这两概念是理解一些动画的基础,例如Android中的补间动画。在一些场景中,我们可能不会给出一个动画的所有帧,所以将帧分成关键帧和过渡帧。关键帧可以理解为一个动画的起始状态,而过渡帧则是系统自动完成插在关键帧之间的部分。 30 | 31 | ![关键帧与过渡帧](http://whysodiao.com/images/frame-intro.jpg) 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 | ![帧数不同的感觉](http://whysodiao.com/images/fps.gif) 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 | ![补间动画](http://whysodiao.com/images/animation.jpg) 194 | 195 | AnimationController具体的源码不做分析了,可以看到Flutter的动画实现的其实是相当的原始,AnimationController需要一个触发刷新的回调,输出也是值的改变,并不像成熟平台里面的配合View去做动画。 196 | 197 | ## 3. Physics库 198 | 199 | Physics库基本上就是插值器的实现部分,这部分比较简单 200 | 201 | ![Physics动画库](http://whysodiao.com/images/physic_lib.jpg) 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 | ![](http://whysodiao.com/images/flutter-platform-channels.png) 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 | ![State LifeCycle](http://whysodiao.com/images/State%20LifeCycle.png) 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 | ![StatefulWidget和StatelessWidget](http://whysodiao.com/images/StatefulWidget和StatelessWidget.png) 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 | ![拆解](http://whysodiao.com/images/combine%20layout.jpg) 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 | ![实际运行效果](http://whysodiao.com/images/device%20run.png) 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 | ![Container属性用法](http://whysodiao.com/images/container-demo-1.png) 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 | ![Container样例](http://whysodiao.com/images/container-demo-2.gif) 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 | ![Flutter In Android](http://whysodiao.com/images/flutter-in-android.gif) 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 | --------------------------------------------------------------------------------