flutter listview 滚动到底部_Flutter 中文文档:Flutter 中的布局
要点widgets 是用于构建 UI 的类。widgets 可以用于布局和展示 UI 元素。通过组合简单的 widgets 来构建复杂的 widgets。Flutter 布局的核心机制是 widgets。在 Flutter 中,几乎所有东西都是 widget —— 甚至布局模型都是 widgets。你在 Flutter 应用程序中看到的图像,图标和文本都是 widgets。此外不能直接看...

要点
-
widgets 是用于构建 UI 的类。
-
widgets 可以用于布局和展示 UI 元素。
-
通过组合简单的 widgets 来构建复杂的 widgets。
Flutter 布局的核心机制是 widgets。在 Flutter 中,几乎所有东西都是 widget —— 甚至布局模型都是 widgets。你在 Flutter 应用程序中看到的图像,图标和文本都是 widgets。此外不能直接看到的也是 widgets,例如用来排列、限制和对齐可见 widgets 的行、列和网格。
你可以通过组合 widgets 来构建更复杂的 widgets 来创建布局。比如,下面第一个截图上有 3 个图标,每个图标下面都有一个标签:

第二个截图显示了可视布局,可以看到有一排三列,其中每列包含一个图标和一个标签。
备忘
本教程中的大多数截图都是将 debugPaintSizeEnabled 设置为 true 以后的效果,因此你可以看到可视布局。更多信息可以查看文档中的 可视化调试,它是 调试 Flutter 应用 中的一节。
以下是这个 UI 的 widget 树形图:

图上大部分应该和你预想的一样,但你可能会疑惑 containers(图上粉色显示的)是什么。Container 是一个 widget,允许你自定义其子 widget。举几个例子,如果要添加 padding、margin、边框或背景颜色,你就可以用上 Container 了。
在这个例子中,每个 Text widget 都被放在一个 Container 以添加 padding。整个 Row 也被放在一个 Container 中,以便添加 padding。
这个例子其余部分的 UI 由属性控制。通过 Icon 的 color 属性来设置它的颜色,通过 Text.style 属性来设置文字的字体、颜色、字重等等。列和行有一些属性可以让你指定子项垂直或水平的对齐方式以及子项应占用的空间大小。
1. 布局 widget
如何在 Flutter 中布局单个 widget?本节将介绍如何创建和显示单个 widget。本节还包括一个简单的 Hello World app 的完整代码。
在 Flutter 中,只需几步就可以在屏幕上显示文本、图标或图像。
1.1 选择一个布局 widget
根据你想要对齐或限制可见 widget 的方式从各种 layout widgets 中进行选择,因为这些特性通常会传递它所给包含的 widget。
本例使用将其内容水平和垂直居中的 Center。
1.2 创建一个可见 widget
举个例子,创建一个 Text widget:
Text('Hello World'),
创建一个 Image widget:
Image.asset(
'images/lake.jpg',
fit: BoxFit.cover,
),
创建一个 Icon widget:
Icon(
Icons.star,
color: Colors.red[500],
),1.3 将可见 widget 添加到布局 widget
所有布局 widgets 都具有以下任一项:
-
一个
child属性,如果它们只包含一个子项 —— 例如Center和Container -
一个
children属性,如果它们包含多个子项 —— 例如Row、Column、ListView和Stack
将 Text widget 添加进 Center widget:
Center(
child: Text('Hello World'),
),
1.4 将布局 widget 添加到页面
一个 Flutter app 本身就是一个 widget,大多数 widgets 都有一个 build() 方法,在 app 的 build() 方法中实例化和返回一个 widget 会让它显示出来。
对于 Material app,你可以使用 Scaffold widget,它提供默认的 banner、背景颜色,还有用于添加抽屉、提示条和底部列表弹窗的 API。你可以将 Center widget 直接添加到主页 body 的属性中。
lib/main.dart (MyApp)
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter layout demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter layout demo'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}
}
备忘
Material 库 实现了一些遵循 Material Design 原则的 widgets。在设计 UI 时,你可以只使用标准 widgets 库 中的 widgets,也可以使用 Material library 中的 widgets。你可以混合来自两个库的 widgets,可以自定义现有 widgets,也可以构建自己的一组自定义 widgets。
1.5 非 Material apps
对于非 Material app,你可以将 Center widget 添加到 app 的 build() 方法里:
lib/main.dart (MyApp)
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.white),
child: Center(
child: Text(
'Hello World',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 32,
color: Colors.black87,
),
),
),
);
}
}
默认情况下,非 Material app 不包含 AppBar、标题和背景颜色。如果你希望在非 Material app 中使用这些功能,则必须自己构建它们。以上 app 将背景颜色更改为白色,将文本更改为深灰色来模拟一个 Material app。
完成! 启动这个 app,你应该能看到 Hello World。
App 源码:
-
Material app:https://github.com/cfug/flutter.cn/tree/master/examples/layout/base
-
非 Material app:https://github.com/cfug/flutter.cn/tree/master/examples/layout/non_material

2. 横向或纵向布局多个 widgets
最常见的布局模式之一是垂直或水平 widgets。你可以使用 Row widget 水平排列 widgets,使用 Column widget 垂直排列 widgets。
要点
- Row 和 Column 是两种最常用的布局模式。
- Row 和 Column 每个都有一个子 widgets 列表。
- 一个子 widget 本身可以是 Row、Column 或其他复杂 widget。
- 可以指定 Row 或 Column 如何在垂直和水平方向上对齐其子项。
- 可以拉伸或限制特定的子 widgets。
- 可以指定子 widgets 如何占用 Row 或 Column 的可用空间。
左侧列的 widget 树嵌套着行和列。
你将在 嵌套行和列 中实现蛋糕介绍示例的一些布局代码。备忘Row 和 Column 是水平和垂直布局的基本原始 widgets —— 这些低级 widgets 允许最大程度的自定义。Flutter 还提供专门的、更高级别的 widgets,可能可以直接满足需求。例如,和 Row 相比你可能更喜欢 ListTile,这是一个易于使用的 widget,有属性可以设置头尾图标,最多可以显示 3 行文本;和 Column 相比你也可能更喜欢 ListView,这是一种类似于列的布局,但如果其内容太长导致可用空间不够容纳时会自动滚动。
2.1 对齐 widgets
你可以使用mainAxisAlignment 和 crossAxisAlignment 属性控制行或列如何对齐其子项。对于一行来说,主轴水平延伸,交叉轴垂直延伸。对于一列来说,主轴垂直延伸,交叉轴水平延伸。

Image.asset 来显示图像。更多信息可以查看本例的 pubspec.yaml 文件,或文档:添加资源和图片。如果你正在使用 Image.network 引用在线图像,则不需要这些操作。在以下示例中,3 个图像每个都是是 100 像素宽。渲染框(在本例中是整个屏幕)宽度超过 300 像素,因此设置主轴对齐方式为 spaceEvenly 会将空余空间在每个图像之间、之前和之后均匀地划分。
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
);
App 源码:https://github.com/cfug/flutter.cn/tree/master/examples/layout/row_column列的工作方式与行的工作方式相同。以下示例展示了包含 3 个图像的列,每个图像的高度为 100 像素。渲染框(在本例中是整个屏幕)高度超过 300 像素,因此设置主轴对齐方式为 spaceEvenly 会将空余空间在每个图像之间、之上和之下均匀地划分。
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],
);

App 源码:
https://github.com/cfug/flutter.cn/tree/master/examples/layout/row_column
2.2 调整 widgets 大小当某个布局太大而超出屏幕时,受影响的边缘会出现黄色和黑色条纹图案。这里有一个行太宽的 例子
通过使用 Expanded widget,可以调整 widgets 的大小以适合行或列。要修复上一个图像行对其渲染框来说太宽的示例,可以用 Expanded widget 把每个图像包起来。
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Image.asset('images/pic1.jpg'),
),
Expanded(
child: Image.asset('images/pic2.jpg'),
),
Expanded(
child: Image.asset('images/pic3.jpg'),
),
],
);
App 源码: https://github.com/cfug/flutter.cn/tree/master/examples/layout/sizing也许你想要一个 widget 占用的空间是兄弟项的两倍。为了达到这个效果,可以使用 Expanded widget 的 flex 属性,这是一个用来确定 widget 的弹性系数的整数。默认的弹性系数为 1。以下代码将中间图像的弹性系数设置为 2:
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'),
),
],
);
App 源码: https://github.com/cfug/flutter.cn/tree/master/examples/layout/sizing
2.3 组合 widgets
默认情况下,行或列沿其主轴会占用尽可能多的空间,但如果要将子项紧密组合在一起,请将其mainAxisSize 设置为 MainAxisSize.min。以下示例使用此属性将星形图标组合在一起。
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),
],
)

2.4 嵌套行和列
布局框架允许你根据需要在行和列内嵌套行和列。让我们看看以下布局的概述部分的代码:
概述的部分实现为两行,评级一行包含五颗星和评论的数量,图标一行包含由图标与文本组成的三列。以下是评级行的 widget 树形图:
ratings 变量创建了一个行,其中包含较小的由 5 个星形图标和文本组成的一行:
var stars = 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),
],
);
final ratings = Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
stars,
Text(
'170 Reviews',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 20,
),
),
],
),
);
小提示为了最大限度地减少高度嵌套的布局代码可能导致的视觉混乱,可以在变量和函数中实现 UI 的各个部分。评级行下方的图标行包含 3 列,每列包含一个图标和两行文本,你可以在其 widget 树中看到:
iconList 变量定义了图标行:
final descTextStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 18,
height: 2,
);
// DefaultTextStyle.merge() allows you to create a default text
// style that is inherited by its child and all subsequent children.
final iconList = DefaultTextStyle.merge(
style: descTextStyle,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Icon(Icons.kitchen, color: Colors.green[500]),
Text('PREP:'),
Text('25 min'),
],
),
Column(
children: [
Icon(Icons.timer, color: Colors.green[500]),
Text('COOK:'),
Text('1 hr'),
],
),
Column(
children: [
Icon(Icons.restaurant, color: Colors.green[500]),
Text('FEEDS:'),
Text('4-6'),
],
),
],
),
),
);leftColumn 变量包含评级和图标行,以及蛋糕介绍的标题和文本:
final leftColumn = Container(
padding: EdgeInsets.fromLTRB(20, 30, 20, 20),
child: Column(
children: [
titleText,
subTitle,
ratings,
iconList,
],
),
);左列放置在 Container 中以限制其宽度。最后,UI 由 Card 内的整行(包含左列和图像)构成。蛋糕图片 来自 Pixabay。你可以使用 Image.network() 从网络上引用图像,但是在本例图像将保存到项目中的一个图像目录中,添加到 pubspec 文件,并使用 Images.asset() 访问。
body: Center(
child: Container(
margin: EdgeInsets.fromLTRB(0, 40, 0, 30),
height: 600,
child: Card(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 440,
child: leftColumn,
),
mainImage,
],
),
),
),
),
小提示蛋糕介绍的例子在宽屏设备(如平板电脑)上横向运行效果最佳。如果在 iOS 模拟器中运行这个示例,可以使用 Hardware > Device 菜单选择不同的设备。在本例中,我们推荐 iPad Pro。你可以使用 Hardware > Rotate 将其方向更改为横向模式。你还可以使用 Window > Scale 更改模拟器窗口的大小(不改变逻辑像素的数量)。
App 源码: https://github.com/cfug/flutter.cn/tree/master/examples/layout/pavlova
3. 通用布局 widgets
Flutter 有一个丰富的布局 widget 仓库,里面有很多经常会用到的布局 widget。目的是为了让你更快的上手,而不是被一个完整的列表吓跑。关于其他有用的 widget 的信息,可以参考 Widget 目录,或者使用 API 参考文档 中的搜索框。而且,API 文档中的 widget 页面中经常会给出一些关于相似的 widget 哪个会更适合你的建议。下面的 widget 会分为两类:widgets 库 中的标准 widgets 和 Material 库 中的 widgets。任何 app 都可以使用 widget 库,但是 Material 库中的组件只能在 Material app 中使用。3.1 标准 widgets
- Container:向 widget 增加 padding、margins、borders、background color 或者其他的装饰。
- GridView:将 widget 展示为一个可滚动的网格。
- ListView:将 widget 展示为一个可滚动的列表。
- Stack:将 widget 覆盖在另一个的上面。
- Card:将相关信息整理到一个有圆角和阴影的盒子中。
- ListTile:将最多三行的文本、可选的导语以及后面的图标组织在一行中。
Container 中,并且改变它的背景色或者图片,来改变设备的背景。摘要 (Container)
- 增加 padding、margins、borders
- 改变背景色或者图片
- 只包含一个子 widget,但是这个子 widget 可以是行、列或者是 widget 树的根 widget
示例 (Container)这个布局包含一个有两行的列,每行有两张图片。Container 用来将列的背景色变为浅灰色。
Widget _buildImageColumn() => Container(
decoration: BoxDecoration(
color: Colors.black26,
),
child: Column(
children: [
_buildImageRow(1),
_buildImageRow(3),
],
),
);

Container 还用来为每个图片添加圆角和外边距:
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),
],
);App 源码: https://github.com/cfug/flutter.cn/tree/master/examples/layout/container
3.4 GridView
使用 GridView 将 widget 作为二维列表展示。GridView 提供两个预制的列表,或者你可以自定义网格。当 GridView 检测到内容太长而无法适应渲染盒时,它就会自动支持滚动。
摘要 (GridView)
- 在网格中使用 widget
- 当列的内容超出渲染容器的时候,它会自动支持滚动。
- 创建自定义的网格,或者使用下面提供的网格的其中一个:
GridView.count允许你制定列的数量GridView.extent允许你制定单元格的最大宽度
示例 (GridView)
-
使用
GridView.extent创建一个最大宽度为 150 像素的网格。
App 源码:
https://github.com/cfug/flutter.cn/tree/master/examples/layout/grid_and_list
-
使用
GridView.count创建一个网格,它在竖屏模式下有两行,在横屏模式下有三行。可以通过为每个 GridTile 设置footer属性来创建标题。
Dart 代码: https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/material/grid_list_demo.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')));
3.5 ListView
ListView,一个和列很相似的 widget,当内容长于自己的渲染盒时,就会自动支持滚动。摘要 (ListView)- 一个用来组织盒子中列表的专用 Column
- 可以水平或者垂直布局
- 当监测到空间不足时,会提供滚动
-
比
Column的配置少,使用更容易,并且支持滚动
使用 ListView 的业务列表,它使用了多个 ListTile。Divider 将餐厅从剧院中分隔开。App 源码: https://github.com/cfug/flutter.cn/tree/master/examples/layout/grid_and_list
使用 ListView 展示特定颜色系列 Material Design 调色板 中的 颜色Dart 代码:https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/colors_demo.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('Kescaped_code#39;s Kitchen', '757 Monterey Blvd', Icons.restaurant),
_tile('Emmyescaped_code#39;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],
),
);3.6 Stack可以使用 Stack 在基础 widget(通常是图片)上排列 widget。widget 可以完全或者部分覆盖基础 widget。摘要 (Stack)
- 用于覆盖另一个 widget
- 子列表中的第一个 widget 是基础 widget;后面的子项覆盖在基础 widget 的顶部
Stack的内容是无法滚动的- 你可以编辑超出渲染框的子项
在 CircleAvatar 的上面使用 Stack 覆盖 Container(在透明的黑色背景上展示它的 Text)。Stack 使用 alignment 属性和 Alignment 让文本偏移。App 源码:https://github.com/cfug/flutter.cn/tree/master/examples/layout/card_and_stack
使用 Stack 将渐变叠加到图片的顶部。渐变可以将工具栏的图标和图片区分开来。Dart 代码:https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/contacts_demo.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,
),
),
),
],
);3.7 CardMaterial 库 中的 Card 包含相关有价值的信息,几乎可以由任何 widget 组成,但是通常和 ListTile 一起使用。 Card 只有一个子项,这个子项可以是列、行、列表、网格或者其他支持多个子项的 widget。默认情况下,Card 的大小是 0x0 像素。你可以使用 SizedBox 控制 card 的大小。在 Flutter 中,Card 有轻微的圆角和阴影来使它具有 3D 效果。改变 Card 的 elevation 属性可以控制阴影效果。例如,把 elevation 设置为 24,可以从视觉上更多的把 Card 抬离表面,使阴影变得更加分散。关于支持的 elevation 的值的列表,可以查看 Material guidelines 中的 Elevation。使用不支持的值则会使阴影无效。摘要 (Card)
- 实现一个 Material card
- 用于呈现相关有价值的信息
- 接收单个子项,但是子项可以是
Row、Column或者其他可以包含列表子项的 widget - 显示圆角和阴影
Card的内容无法滚动- 来自 Material 库
包含 3 个 ListTile 的 Card,并且通过被 SizedBox 包住来调整大小。Divider 分隔了第一个和第二个 ListTiles。App 源码:https://github.com/cfug/flutter.cn/tree/master/examples/layout/card_and_stack
包含图片和文本的 Card。Dart 代码:https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/material/cards_demo.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],
),
),
],
),
),
);3.8 ListTileListTile 是 Material 库 中专用的行 widget,它可以很轻松的创建一个包含三行文本以及可选的行前和行尾图标的行。ListTile 在 Card 或者 ListView 中最常用,但是也可以在别处使用。摘要 (ListTile)
- 一个可以包含最多 3 行文本和可选的图标的专用的行
- 比
Row更少的配置,更容易使用 - 来自 Material 库
包含 3 个 ListTiles 的 Card。App 源码:https://github.com/cfug/flutter.cn/tree/master/examples/layout/card_and_stack
使用 ListTile 列出 3 个下拉按钮类型。Dart 代码:https://github.com/flutter/flutter/blob/master/examples/flutter_gallery/lib/demo/material/buttons_demo.dart4. 其他资源当写布局代码时,下面的资源可能会帮助到你。
- Layout 教程学习如何构建布局。
链接:https://flutter.cn/docs/development/ui/layout/tutorial
- 核心 Widget 目录描述了 Flutter 中很多可用的 widget。
链接:https://flutter.cn/docs/development/ui/widgets
- 给 Web 开发者的 Flutter 指南对那些熟悉 web 开发的人来说,这页将 HTML/CSS 的功能映射到 Flutter 特性上。
- Flutter GalleryDemo app 展示了很多 Material Design widget 和其他的 Flutter 特性。链接:https://github.com/flutter/flutter/tree/master/examples/flutter_gallery
- Flutter API 文档所有 Flutter 库的参考文档。链接:https://api.flutter-io.cn/index.html
- 处理边界约束 (Box constraints) 的问题讨论 widget 是如何受渲染框限制的。链接:https://flutter.cn/docs/development/ui/layout/box-constraints
- 在 Flutter 中添加资源和图片解释在你的 app 中如何添加图片和其他资源。链接:https://flutter.cn/docs/development/ui/assets-and-images
- Flutter 从 0 到 1一位开发者第一次写 Flutter app 的经验分享文章。链接:https://medium.com/@mravn/zero-to-one-with-flutter-43b13fd7b354

更多推荐



所有评论(0)