# flutter-docs **Repository Path**: wongsi/flutter-docs ## Basic Information - **Project Name**: flutter-docs - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-26 - **Last Updated**: 2021-03-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Flutter 2.0 快速上手 ## 安装Flutter SDK Flutter SDK下载地址: https://flutter.dev/docs/development/tools/sdk/releases ### MacOS环境 编辑~/.bash_profile文件 ``` export FLUTTER_HOME=/Applications/flutter export PATH=$PATH:$FLUTTER_HOME/bin export PATH=$PATH:$FLUTTER_HOME/bin/cache/dart-sdk/bin export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn ``` ### Windows环境 * 在Path中添加Flutter SDK目录下bin目录以及Dart的目录 * 新建变量 PUB_HOSTED_URL,其值为https://pub.flutter-io.cn * 新建变量 FLUTTER_STORAGE_BASE_URL,其值为https://storage.flutter-io.cn ## 极速体验 > 使用 Flutter 构建 Web 应用 1. 安装[Flutter SDK](https://flutter.dev/docs/get-started/install/windows) 2. 安装Chrome浏览器 3. `flutter create myapp` 创建App 4. `flutter run -d chrome` 浏览器上可看到初始程序。 5. `cd myapp` 进入App目录 6. 编辑 lib/main.dart, 替换成: ```dart import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'MyApp', home: Scaffold( appBar: AppBar( title: Text('Welcome to Flutter'), ), body: Container( child: Text("Hello World!"), ), ), ); } } ``` 7. `Container`替换成`Center`试试,控制台按下"r",热重启后文字居中啦! 8. `flutter build web` 生成发行版,THE END😄 ## Widgets 介绍 * Widgets 是用于构建 UI 的类。 * Widgets 可以用于布局和展示 UI 元素。 * 通过组合简单的 widgets 来构建复杂的 widgets。 ### 可见 widget * `Text('Hello World'),` * `Image.asset('images/lake.jpg',fit: BoxFit.cover),` * `Icon(Icons.star, color: Colors.red[500],),` #### Image.network() 从网络上引用图像 #### Images.asset() 1. 创建 `assets/images/` 目录,将图片拷贝进来。 2. 编辑 `pubspec.yaml` 定义图像目录 ``` flutter: assets: - assets/images/ ``` 3. 访问目录中图像, 代码示例 ```dart Image.asset( 'assets/images/empty_home_bg.png', width: 600, height: 240, // 图片等比缩小到刚好能够覆盖住整个渲染 box fit: BoxFit.cover, ) ``` ### 布局 widget 所有布局 widgets 都具有以下任一项: * 一个 child 属性,如果它们只包含一个子项 —— 例如 Center 和 Container * 一个 children 属性,如果它们包含多个子项 —— 例如 Row、Column、ListView 和 Stack 将 Text widget 添加进 Center widget: ```dart Center( child: Text('Hello World'), ), ``` ### 将布局 widget 添加到页面 ```dart import 'package:flutter/material.dart'; void main() { /// 这里主要使用 material.dart 中的 AppBar 和 Scaffold widget /// 许多 Material Design 的 widget 需要在 MaterialApp 中才能显现正常。 /// 因此使用 MaterialApp 运行应用。 runApp(MaterialApp( title: 'Flutter Tutorial', // used by the OS task switcher home: TutorialHome())); } /// Widget 的主要工作是实现 build方法 class TutorialHome extends StatelessWidget { @override Widget build(BuildContext context) { /// Material组件之Scaffold布局 /// Scaffold widget 将许多不同的 widget 作为命名参数, /// 每个 widget 都放在了 Scofford 布局中的合适位置。 return Scaffold( appBar: AppBar( leading: IconButton(icon: Icon(Icons.menu), onPressed: null), title: Text('示例'), actions: [ IconButton(icon: Icon(Icons.search), onPressed: null), ], ), body: Center(child: Text('Hello, world!')), floatingActionButton: FloatingActionButton( tooltip: 'Add', // used by assistive technologies child: Icon(Icons.add), onPressed: null), ); } } ``` 同 Scaffold,AppBar允许我们给 leading、title、actions 传递 widget。 这种模式在整个框架会中重复出现,在设计自己的 widget 时可以考虑这种模式。 > 为了最大限度地减少高度嵌套的布局代码可能导致的视觉混乱,可以在变量和函数中实现 UI 的各个部分。 ### 横向或纵向布局 * `Row` 水平排列 widgets, * `Column` 垂直排列 widgets。 ![水平线性布局](https://flutter.cn/assets/ui/layout/pavlova-diagram-8b3d410640d9b3575d69c0724203c5dff6814fdd1a57546a5f65f98254077a92.png) ![垂直线性布局](https://flutter.cn/assets/ui/layout/column-diagram-4e2ce8e33c32a09d090280fb7b2925baaf58f6de7876a551c207ab904e2fafc6.png) ### 对齐 widgets 控制行或列如何对齐其子项的属性 * mainAxisAlignment 主轴对齐 * crossAxisAlignment 交叉轴对齐 对于一行来说,主轴水平延伸,交叉轴垂直延伸。 对于一列来说,主轴垂直延伸,交叉轴水平延伸。 ![对齐](https://flutter.cn/assets/ui/layout/row-diagram-ad51795e19e3e1d412815b287c9caa694ad357892e3ab8b3ef1da0c4c6e011db.png) 这两个类提供了很多用于控制对齐的常量,如:MainAxisAlignment.spaceEvenly。 ```dart Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Image.asset('images/pic1.jpg'), Image.asset('images/pic2.jpg'), Image.asset('images/pic3.jpg'), ], ); ``` ### 调整 widgets 大小 当某个布局太大而超出屏幕时,受影响的边缘会出现黄色和黑色条纹图案。 * `Expanded` 可以调整 widgets 的大小以适合行或列。 * flex 属性, 一个用来确定 widget 的弹性系数的整数 比如,图片宽度超过屏幕宽度,需要自适应可使用`Expanded`包装。 多个图片的行占比可使用flex 属性调整。 ```dart Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Image.asset('images/pic1.jpg'), ), Expanded( flex: 2, child: Image.asset('images/pic2.jpg'), ), Expanded( child: Image.asset('images/pic3.jpg'), ), ], ); ``` ### 组合 widgets 默认情况下,行或列沿其主轴会占用尽可能多的空间 要将子项紧密组合在一起,将MainAxisSize.min ```dart Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.green[500]), Icon(Icons.star, color: Colors.black), Icon(Icons.star, color: Colors.black), ], ) ``` ## 通用布局--标准 widgets * Container:向 widget 增加 padding、margins、borders、background color 或者其他的“装饰”。 * GridView:将 widget 展示为一个可滚动的网格。 * ListView:将 widget 展示为一个可滚动的列表。 * Stack:将 widget 覆盖在另一个的上面。 ### Container 许多布局都可以随意的用`Container`, 它可以将使用了 padding 或者增加了 borders/margins 的 widget 分开。 你可以通过将整个布局放到一个 Container 中, 并且改变它的背景色或者图片,来改变设备的背景。 * 增加 padding、margins、borders * 改变背景色或者图片 * 只包含一个子 widget,但是这个子 widget 可以是行、列或者是 widget 树的根 widget Container 用来为每个图片添加圆角和外边距: ```dart Widget _buildDecoratedImage(int imageIndex) => Expanded( child: Container( decoration: BoxDecoration( border: Border.all(width: 10, color: Colors.black38), borderRadius: const BorderRadius.all(const Radius.circular(8)), ), margin: const EdgeInsets.all(4), child: Image.asset('images/pic$imageIndex.jpg'), ), ); Widget _buildImageRow(int imageIndex) => Row( children: [ _buildDecoratedImage(imageIndex), _buildDecoratedImage(imageIndex + 1), ], ); ``` ### GridView 使用 GridView 将 widget 作为二维列表展示。 GridView 提供两个预制的列表,或者你可以自定义网格。当 GridView 检测到内容太长而无法适应渲染盒时,它就会自动支持滚动。 * 在网格中使用 widget * 当列的内容超出渲染容器的时候,它会自动支持滚动。 * 创建自定义的网格,或者使用下面提供的网格的其中一个: * GridView.count 允许你制定列的数量 * GridView.extent 允许你制定单元格的最大宽度 使用 GridView.extent 创建一个最大宽度为 150 像素的网格。 ```dart Widget _buildGrid() => GridView.extent( maxCrossAxisExtent: 150, padding: const EdgeInsets.all(4), mainAxisSpacing: 4, crossAxisSpacing: 4, children: _buildGridTileList(30)); // The images are saved with names pic0.jpg, pic1.jpg...pic29.jpg. // The List.generate() constructor allows an easy way to create // a list when objects have a predictable naming pattern. List _buildGridTileList(int count) => List.generate( count, (i) => Container(child: Image.asset('images/pic$i.jpg'))); ``` ### ListView 一个和列很相似的 widget,当内容长于自己的渲染盒时,就会自动支持滚动。 * 一个用来组织盒子中列表的专用 Column * 可以水平或者垂直布局 * 当监测到空间不足时,会提供滚动 * 比 Column 的配置少,使用更容易,并且支持滚动 使用 ListView 的业务列表,它使用了多个 ListTile。Divider 将餐厅从剧院中分隔开。 ```dart Widget _buildList() => ListView( children: [ _tile('CineArts at the Empire', '85 W Portal Ave', Icons.theaters), _tile('The Castro Theater', '429 Castro St', Icons.theaters), _tile('Alamo Drafthouse Cinema', '2550 Mission St', Icons.theaters), _tile('Roxie Theater', '3117 16th St', Icons.theaters), _tile('United Artists Stonestown Twin', '501 Buckingham Way', Icons.theaters), _tile('AMC Metreon 16', '135 4th St #3000', Icons.theaters), Divider(), _tile('K\'s Kitchen', '757 Monterey Blvd', Icons.restaurant), _tile('Emmy\'s Restaurant', '1923 Ocean Ave', Icons.restaurant), _tile( 'Chaiya Thai Restaurant', '272 Claremont Blvd', Icons.restaurant), _tile('La Ciccia', '291 30th St', Icons.restaurant), ], ); ListTile _tile(String title, String subtitle, IconData icon) => ListTile( title: Text(title, style: TextStyle( fontWeight: FontWeight.w500, fontSize: 20, )), subtitle: Text(subtitle), leading: Icon( icon, color: Colors.blue[500], ), ); ``` ### Stack 可以使用 Stack 在基础 widget(通常是图片)上排列 widget, widget 可以完全或者部分覆盖基础 widget。 * 用于覆盖另一个 widget * 子列表中的第一个 widget 是基础 widget;后面的子项覆盖在基础 widget 的顶部 * Stack 的内容是无法滚动的 * 你可以剪切掉超出渲染框的子项 在 CircleAvatar 的上面使用 Stack 覆盖 Container (在透明的黑色背景上展示它的 Text)。 Stack 使用 alignment 属性和 Alignment 让文本偏移。 ```dart Widget _buildStack() => Stack( alignment: const Alignment(0.6, 0.6), children: [ CircleAvatar( backgroundImage: AssetImage('images/pic.jpg'), radius: 100, ), Container( decoration: BoxDecoration( color: Colors.black45, ), child: Text( 'Mia B', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ], ); ``` ## 通用布局--Material widgets * Card:将相关信息整理到一个有圆角和阴影的盒子中。 * ListTile:将最多三行的文本、可选的导语以及后面的图标组织在一行中。 ### Card Material 库 中的 Card 包含相关有价值的信息,几乎可以由任何 widget 组成,但是通常和 ListTile 一起使用。 Card 只有一个子项,这个子项可以是列、行、列表、网格或者其他支持多个子项的 widget。默认情况下,Card 的大小是 0x0 像素。你可以使用 SizedBox 控制 card 的大小。 在 Flutter 中,Card 有轻微的圆角和阴影来使它具有 3D 效果。改变 Card 的 elevation 属性可以控制阴影效果。例如,把 elevation 设置为 24,可以从视觉上更多的把 Card 抬离表面,使阴影变得更加分散。关于支持的 elevation 的值的列表,可以查看 Material guidelines 中的 Elevation。使用不支持的值则会使阴影无效。 * 实现一个 Material card * 用于呈现相关有价值的信息 * 接收单个子项,但是子项可以是 Row、Column 或者其他可以包含列表子项的 widget * 显示圆角和阴影 * Card 的内容无法滚动 * 来自 Material 库 ### ListTile ListTile 是 Material 库 中专用的行 widget, 它可以很轻松的创建一个包含三行文本以及可选的行前和行尾图标的行。 ListTile 在 Card 或者 ListView 中最常用,但是也可以在别处使用。 * 一个可以包含最多 3 行文本和可选的图标的专用的行 * 比 Row 更少的配置,更容易使用 * 来自 Material 库 ![示例 (Card)](https://flutter.cn/assets/ui/layout/card-3db18421d33c96f34f8d19889a5f28c61287063a62c9770f311fd56aeff0b364.png) 包含 3 个 ListTile 的 Card,并且通过被 SizedBox 包住来调整大小。 Divider 分隔了第一个和第二个 ListTiles。 ```dart Widget _buildCard() => SizedBox( height: 210, child: Card( child: Column( children: [ ListTile( title: Text('1625 Main Street', style: TextStyle(fontWeight: FontWeight.w500)), subtitle: Text('My City, CA 99984'), leading: Icon( Icons.restaurant_menu, color: Colors.blue[500], ), ), Divider(), ListTile( title: Text('(408) 555-1212', style: TextStyle(fontWeight: FontWeight.w500)), leading: Icon( Icons.contact_phone, color: Colors.blue[500], ), ), ListTile( title: Text('costa@example.com'), leading: Icon( Icons.contact_mail, color: Colors.blue[500], ), ), ], ), ), ); ``` ### Widget目录 官网参考 [Widget目录](https://flutter.cn/docs/development/ui/widgets) ## 布局构建 see https://flutter.cn/docs/development/ui/layout/tutorial ```dart import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { Color color = Theme.of(context).primaryColor; Widget buttonSection = Container( child: Row( // 将剩余的空间均分到每列各自的前后及中间。 mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildButtonColumn(color, Icons.call, 'CALL'), _buildButtonColumn(color, Icons.near_me, 'ROUTE'), _buildButtonColumn(color, Icons.share, 'SHARE'), ], ), ); Widget textSection = Container( padding: const EdgeInsets.all(32), child: Text( 'Lake Oeschinen lies at the foot of the Blüemlisalp in the Bernese ' 'Alps. Situated 1,578 meters above sea level, it is one of the ' 'larger Alpine Lakes. A gondola ride from Kandersteg, followed by a ' 'half-hour walk through pastures and pine forest, leads you to the ' 'lake, which warms to 20 degrees Celsius in the summer. Activities ' 'enjoyed here include rowing, and riding the summer toboggan run.', // 文本将在填充满列宽后在单词边界处自动换行 softWrap: true, ), ); Widget titleSection = Container( padding: const EdgeInsets.all(32), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.only(bottom: 8), child: Text( "你的名字", style: TextStyle(fontWeight: FontWeight.bold), ), ), Text( "你的职务", style: TextStyle(color: Colors.grey[400]), ), ], ), ), FavoriteWidget() ], ), ); return MaterialApp( title: 'My App', home: Scaffold( appBar: AppBar( title: Text('Flutter layout'), ), body: ListView( children: [ Image.asset( 'assets/images/empty_home_bg.png', width: 600, height: 240, // 图片尽可能等比缩小到刚好能够覆盖住整个渲染 box fit: BoxFit.cover, ), titleSection, buttonSection, textSection, ], ), ), ); } Column _buildButtonColumn(Color color, IconData icon, String label) { return Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: color), Container( // 仅有上间距,使得文本与图标分隔开。 margin: const EdgeInsets.only(top: 8), child: Text( label, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w400, color: color, ), ), ), ], ); } } /// 有状态的 widgets class FavoriteWidget extends StatefulWidget { FavoriteWidget({Key key}) : super(key: key); @override _FavoriteWidgetState createState() => _FavoriteWidgetState(); } class _FavoriteWidgetState extends State { bool _isFavorited = true; int _favoriteCount = 41; @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ Container( padding: EdgeInsets.all(0), child: IconButton( padding: EdgeInsets.all(0), alignment: Alignment.centerRight, icon: (_isFavorited ? Icon(Icons.star) : Icon(Icons.star_border)), color: Colors.red[500], onPressed: _toggleFavorite, ), ), // 数值变化时,文本宽度会有明显的‘跳跃’ // 使用SizeBox并设置其宽度可防止 SizedBox( width: 18, child: Container( child: Text('$_favoriteCount'), ), ), ], ); } void _toggleFavorite() { setState(() { if (_isFavorited) { _favoriteCount -= 1; _isFavorited = false; } else { _favoriteCount += 1; _isFavorited = true; } }); } } ``` ## 布局约束 熟记这条规则: 1. 首先,上层 widget 向下层 widget 传递约束条件; 2. 然后,下层 widget 向上层 widget 传递大小信息。 3. 最后,上层 widget 决定下层 widget 的位置。 更多细节: * Widget 会通过它的 父级 获得自身的约束。约束实际上就是 4 个浮点类型的集合:最大/最小宽度,以及最大/最小高度。 * 然后,这个 widget 将会逐个遍历它的 children 列表。向子级传递 约束(子级之间的约束可能会有所不同),然后询问它的每一个子级需要用于布局的大小。 * 然后,这个 widget 就会对它子级的 children 逐个进行布局。(水平方向是 x 轴,竖直是 y 轴) * 最后,widget 将会把它的大小信息向上传递至父 widget(包括其原始约束条件)。 see https://flutter.cn/docs/development/ui/layout/constraints see https://flutter.cn/docs/development/ui/layout/box-constraints ## Tips ### enable null safety * `cd myapp` * `dart migrate --apply-changes` ### 初始化 * `flutter channel stable` * `flutter upgrade` ### 打开桌面应用开发 * `flutter config --enable-macos-desktop` * `flutter config --enable-windows-desktop` * `flutter config --enable-linux-desktop` ### 第一个 Flutter 应用示例代码 ```dart import 'dart:math'; import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { const MyApp({Key key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'MyApp', home: Scaffold( appBar: AppBar( title: Text('Welcome to Flutter'), ), body: Center( child: RandomWords(), ), ), ); } } class RandomWords extends StatefulWidget { @override _RandomWordsState createState() => _RandomWordsState(); } class _RandomWordsState extends State { final _suggestions = []; final _biggerFont = TextStyle(fontSize: 18.0); @override Widget build(BuildContext context) { // return Text(Random().nextInt(100).toString()); return _buildSuggestions(); } Widget _buildSuggestions() { return ListView.builder( padding: EdgeInsets.all(16.0), itemBuilder: (context, i) { if (i.isOdd) return Divider(); final index = i ~/ 2; if (index >= _suggestions.length) { for (var i = 0; i < 11; i++) { _suggestions.add(Random().nextInt(100).toString()); } } return _buildRow(_suggestions[index]); }); } Widget _buildRow(text) { return ListTile( title: Text( text, style: _biggerFont, ), ); } } ``` ### IOS开发 安装XCODE ### Android开发 安装Android Studio 下载:https://developer.android.com/studio/?utm_source=android-studio ## 参考 [Building Beautiful UIs with Flutter](https://codelabs.flutter-io.cn/codelabs/flutter/) https://codelabs.flutter-io.cn/ https://github.com/flutter/gallery/ https://pub.flutter-io.cn/flutter/packages?platform=web