了解如何创建 Flutter 应用的界面以及如何在它们之间进行数据传递。
Flutter 是一个流行的开源工具包,它可用于构建跨平台的应用。在文章《用 Flutter 创建移动应用》中,我已经向大家展示了如何在 Linux 中安装 Flutter 并创建你的第一个应用。而这篇文章,我将向你展示如何在你的应用中添加一个列表,点击每一个列表项可以打开一个新的界面。这是移动应用的一种常见设计方法,你可能以前见过的,下面有一个截图,能帮助你对它有一个更直观的了解:
Flutter 使用 Dart 语言。在下面的一些代码片段中,你会看到以斜杠开头的语句。两个斜杠(//
)是指代码注释,用于解释某些代码片段。三个斜杠(///
)则表示的是 Dart 的文档注释,用于解释 Dart 类和类的属性,以及其他的一些有用的信息。
查看Flutter应用的主要部分
Flutter 应用的典型入口点是 main()
函数,我们通常可以在文件 lib/main.dart
中找到它:
“`
void main() {
runApp(MyApp());
}
“`
应用启动时,main()
会被调用,然后执行 MyApp()
。 MyApp
是一个无状态微件(StatelessWidget
),它包含了MaterialApp()
微件中所有必要的应用设置(应用的主题、要打开的初始页面等):
“`
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘Flutter Demo’,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: ‘Flutter Demo Home Page’),
);
}
}
“`
生成的 MyHomePage()
是应用的初始页面,是一个有状态的微件,它包含包含可以传递给微件构造函数参数的变量(从上面的代码看,我们传了一个 title
变量给初始页面的构造函数):
“`
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
“`
有状态微件(StatefulWidget
)表示这个微件可以拥有自己的状态:_MyHomePageState
。调用 _MyHomePageState
中的 setState()
方法,可以重新构建用户界面:
“`
class _MyHomePageState extends State
int _counter = 0; // Number of taps on + button.
void _incrementCounter() { // Increase number of taps and update UI by calling setState().
setState(() {
_counter++;
});
}
…
}
“`
不管是有状态的,还是无状态的微件,它们都有一个 build()
方法,该方法负责微件的 UI 外观。
“`
@override
Widget build(BuildContext context) {
return Scaffold( // Page widget.
appBar: AppBar( // Page app bar with title and back button if user can return to previous screen.
title: Text(widget.title), // Text to display page title.
),
body: Center( // Widget to center child widget.
child: Column( // Display children widgets in column.
mainAxisAlignment: MainAxisAlignment.center,
children:
Text( // Static text.
‘You have pushed the button this many times:’,
),
Text( // Text with our taps number.
‘$_counter’, // $ sign allows us to use variables inside a string.
style: Theme.of(context).textTheme.headline4,// Style of the text, “Theme.of(context)” takes our context and allows us to access our global app theme.
),
],
),
),
// Floating action button to increment _counter number.
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: ‘Increment’,
child: Icon(Icons.add),
),
);
}
“`
修改你的应用
一个好的做法是,把 main()
方法和其他页面的代码分开放到不同的文件中。要想将它们分开,你需要右击 lib
目录,然后选择 “New > Dart File” 来创建一个 .dart 文件:
将新建的文件命名为 items_list_page
。
切换回到 main.dart
文件,将 MyHomePage
和 _MyHomePageState
中的代码,剪切并粘贴到我们新建的文件。然后将光标放到 StatefulWidget
上(下面红色的下划线处), 按 Alt+Enter
后出现下拉列表,然后选择 package:flutter/material.dart
:
经过上面的操作我们将 flutter/material.dart
包添加到了 main.dart
文件中,这样我们就可以使用 Flutter 提供的默认的 material 主题微件。
然后, 在类名 MyHomePage
右击,“Refactor > Rename…”将其重命名为 ItemsListPage
:
Flutter 识别到你重命名了 StatefulWidget
类,它会自动将它的 State
类也跟着重命名:
回到 main.dart
文件,将文件名 MyHomePage
改为 ItemsListPage
。 一旦你开始输入, 你的 Flutter 集成开发环境(可能是 IntelliJ IDEA 社区版、Android Studio 和 VS Code 或 VSCodium),会给出自动代码补完的建议。
按回车键即可完成输入,缺失的导入语句会被自动添加到文件的顶部。
到此,你已经完成了初始设置。现在你需要在 lib
目录创建一个新的 .dart 文件,命名为 item_model
。(注意,类命是大写驼峰命名,一般的文件名是下划线分割的命名。)然后粘贴下面的代码到新的文件中:
“`
/// Class that stores list item info:
/// [id] – unique identifier, number.
/// – icon to display in UI.
/// [title] – text title of the item.
/// [description] – text description of the item.
class ItemModel {
// class constructor
ItemModel(this.id, this.icon, this.title, this.description);
// class fields
final int id;
final IconData icon;
final String title;
final String description;
}
“`
回到 items_list_page.dart
文件,将已有的 _ItemsListPageState
代码替换为下面的代码:
“`
class _ItemsListPageState extends State
// Hard-coded list of [ItemModel] to be displayed on our page.
final List
ItemModel(0, Icons.accountbalance, ‘Balance’, ‘Some info’),
ItemModel(1, Icons.accountbalancewallet, ‘Balance wallet’, ‘Some info’),
ItemModel(2, Icons.alarm, ‘Alarm’, ‘Some info’),
ItemModel(3, Icons.mylocation, ‘My location’, ‘Some info’),
ItemModel(4, Icons.laptop, ‘Laptop’, ‘Some info’),
ItemModel(5, Icons.backup, ‘Backup’, ‘Some info’),
ItemModel(6, Icons.settings, ‘Settings’, ‘Some info’),
ItemModel(7, Icons.call, ‘Call’, ‘Some info’),
ItemModel(8, Icons.restore, ‘Restore’, ‘Some info’),
ItemModel(9, Icons.cameraalt, ‘Camera’, ‘Some info’),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView.builder( // Widget which creates [ItemWidget] in scrollable list.
itemCount: items.length, // Number of widget to be created.
itemBuilder: (context, itemIndex) => // Builder function for every item with index.
ItemWidget(items[itemIndex], () {
_onItemTap(context, itemIndex);
}),
));
}
// Method which uses BuildContext to push (open) new MaterialPageRoute (representation of the screen in Flutter navigation model) with ItemDetailsPage (StateFullWidget with UI for page) in builder.
onItemTap(BuildContext context, int itemIndex) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ItemDetailsPage(items[itemIndex])));
}
}
// StatelessWidget with UI for our ItemModel-s in ListView.
class ItemWidget extends StatelessWidget {
const ItemWidget(this.model, this.onItemTap, {Key key}) : super(key: key);
final ItemModel model;
final Function onItemTap;
@override
Widget build(BuildContext context) {
return InkWell( // Enables taps for child and add ripple effect when child widget is long pressed.
onTap: onItemTap,
child: ListTile( // Useful standard widget for displaying something in ListView.
leading: Icon(model.icon),
title: Text(model.title),
),
);
}
}
“`
为了提高代码的可读性,可以考虑将 ItemWidget
作为一个单独的文件放到 lib
目录中。
现在唯一缺少的是 ItemDetailsPage
类。在 lib
目录中我们创建一个新文件并命名为 item_details_page
。然后将下面的代码拷贝进去:
“`
import ‘package:flutter/material.dart’;
import ‘item_model.dart’;
/// Widget for displaying detailed info of [ItemModel]
class ItemDetailsPage extends StatefulWidget {
final ItemModel model;
const ItemDetailsPage(this.model, {Key key}) : super(key: key);
@override
_ItemDetailsPageState createState() => _ItemDetailsPageState();
}
class _ItemDetailsPageState extends State
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.model.title),
),
body: Center(
child: Column(
children: [
const SizedBox(height: 16),
Icon(
widget.model.icon,
size: 100,
),
const SizedBox(height: 16),
Text(
‘Item description: ${widget.model.description}’,
style: TextStyle(fontSize: 18),
)
],
),
),
);
}
}
“`
上面的代码几乎没什么新东西,不过要注意的是 _ItemDetailsPageState
里使用了 widget.item.title
这样的语句,它让我们可以从有状态类中引用到其对应的微件(StatefulWidget
)。
添加一些动画
现在让我们来添加一些基础的动画:
- 找到
ItemWidget
代码块(或者文件) - 将光标放到
build()
方法中的Icon()
微件上 - 按
Alt+Enter
,然后选择“Wrap with widget…”
输入 Hero
,然后从建议的下拉列表中选择 Hero((Key key, @required this, tag, this.create))
:
下一步, 给 Hero 微件添加 tag
属性 tag: model.id
:
最后我们在 item_details_page.dart
文件中做相同的修改:
收尾
这篇教程,让你学到了:
- 一些符合标准的,且能用于自动创建应用的组件。
- 如何添加多个页面以及在页面间传递数据。
- 如何给多个页面添加简单的动画。
如果你想了解更多,查看 Flutter 的 文档(有一些视频和样例项目的链接,还有一些创建 Flutter 应用的“秘方”)与 源码,源码的开源许可证是 BSD 3。
via: https://opensource.com/article/20/11/flutter-lists-mobile-app
作者:Vitaly Kuprenko 选题:lujun9972 译者:ywxgod 校对:wxy
主题测试文章,只做测试使用。发布者:eason,转转请注明出处:https://aicodev.cn/2021/06/01/%e5%9c%a8-flutter-%e7%a7%bb%e5%8a%a8%e5%ba%94%e7%94%a8%e7%a8%8b%e5%ba%8f%e4%b8%ad%e5%88%9b%e5%bb%ba%e4%b8%80%e4%b8%aa%e5%88%97%e8%a1%a8/