给IDE安装插件 我使用vsCode,安装Futter和Dart两个插件,你也可以使用Android Studio IDE或者IntelliJ,也需要安装插件。 创建一个基于模版的Flutter app 在你的项目文件夹下打开终端,输入 flutter create 你的app名称。比如 cd Desktop/www && flutter create firstapp。 用IDE打开项目,你主要编辑的是 lib/main.dart 文件。 Hello World 按照惯例,我们写一个 Hello World。 首先删除 lib/main.dart 内的内容,然后输入
import 'package:flutter/material.dart';
void main() => runApp(new Center( child: new Text('Hello World', textDirection: TextDirection.ltr), ));
终端运行 flutter run,效果如下图 1. 代码中使用了runApp函数,runApp函数接受指定的控件(Widget),并使其作为控件树(widget tree)的根控件。控件(Widget)定义一个元素(Element)的配置,在Flutter框架的层次结构中处于核心层。本实例中,控件树(widget tree)包含两个控件,Center控件使其子控件处于中间位置,Text控件打印文本内容。runApp函数强制将根控件覆盖屏幕,这意味着文本将显示在屏幕中心。 2. main函数使用了(=>)符号, 这是Dart中单行函数或方法的简写。 3. import 'package:flutter/material.dart',引入Flutter提供的一套丰富的Material widgets。 创建一个书单列表应用 1. 定义一个展示类 Book.dart import 'package:flutter/material.dart';
// 声明一个 Book 类 class Book { // 声明一个常量构造函数 const Book({this.name}); final String name; } // 重命名一个具有两个参数的来自父控件的回调函数CartChangedCallback,两参数分别为自定义的Book类和布尔型, typedef void CartChangedCallback(Book book, bool inCart);
// 定义一个扩展自Flutter无状态组件的类 BookItem class BookItem extends StatelessWidget { // 声明构造函数,并调用超类为每一个book实例增加一个key BookItem({Book book, this.inCart, this.onCartChanged}) : book = book, super(key: new ObjectKey(book)); final Book book; final bool inCart; final CartChangedCallback onCartChanged;
// 定义一个颜色方法 Color _getColor(BuildContext context) { return inCart ? Colors.black54 : Theme.of(context).primaryColor; }
// 定义一个文本样式方法 TextStyle _getTextStyle(BuildContext context) { if (!inCart) return null; return new TextStyle( color: Colors.black54, decoration: TextDecoration.lineThrough, ); }
// 重写一个 Widget @override Widget build(BuildContext context) { return new ListTile( onTap: () => onCartChanged(book, !inCart), leading: new CircleAvatar( backgroundColor: _getColor(context), child: new Text(book.name[0]), ), title: new Text(book.name, style: _getTextStyle(context)), ); } }
BookItem控件遵循无状态控件的通用模式,将在构造函数中接收到的值存储在final成员变量中,然后在build函数执行时使用。 当用户点击列表项时,控件不会直接修改inCart的值,但是控件从父控件接收onCartChanged函数。此模式允许你在控件较高的层次结构中存储状态,这样使状态的持续时间更长。在极端情况下,存储在runApp的控件状态在应用程序的生命周期内保持不变。 当父控件接收到onCartChanged回调,父控件会更新其内部状态,这将触发父控件重建并使用新的inCart值创建BookItem的新实例。尽管父控件在重建时创建了BookItem的新实例,但该操作很节省,因为框架会将新构建的控件与之前构建的控件时进行比较,并将有差异的部分应用于底层渲染对象。 2. 编写父控件 mian.dart import 'package:flutter/material.dart'; // 引入Book.dart import 'Book.dart';
// 定义一个扩展自有状态控件的 BookList 类 class BookList extends StatefulWidget { BookList({Key key, this.books}) : super(key: key); final List<Book> books; @override _BookListState createState() => new _BookListState(); } class _BookListState extends State<BookList> { Set<Book> _bookCart = new Set<Book>(); void _handleCartChanged(Book book, bool inCart) { setState( () { if (inCart) _bookCart.add(book); else _bookCart.remove(book); }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text('书单列表'), ), body: new ListView( children: widget.books.map( (Book book) { return new BookItem( book: book, inCart: _bookCart.contains(book), onCartChanged: _handleCartChanged, ); } ).toList(), ), ); } } final List<Book> _kBooks = <Book>[ new Book(name: '你倾城一笑惊绝了多少年华'), new Book(name: '她被你一笑绝经了多少年头'), new Book(name: '我看你俩是昏厥了多少岁月'), new Book(name: '他看着我们懵逼了多少时光'), new Book(name: '所有人梦里花落了多少年少'), ];
void main() => runApp(new MaterialApp( title: '我的应用', home: new BookList(books: _kBooks), ));
类BookList继承了StatefulWidget,这意味着这个控件存储可变状态,当BookList首次插入到树中,框架调用createState函数在树中相关联的位置创建一个新的_BookListState实例。当该控件的父控件重建时,父控件会创建一个新的BookList实例,但框架将重用已经在树上的_BookListState实例,而不会再次调用createState函数。 要访问当前BookList的属性,_BookListState可以使用其config属性。如果父控件重建并创建一个新的BookList,_BookListState也将使用新的config值重建。如果想要在config属性更改时收到通知,可以覆盖didUpdateConfig函数,它通过oldConfig将旧配置与当前config进行比较。 当处理onCartChanged回调时,_BookListState通过从_bookCart中添加或删除一个产品来改变其内部状态。为了向框架通知它改变了内部状态,它在setState中封装这些调用。调用setState将控件标记为dirty,并安排它在应用程序下次需要更新屏幕时重建。如果在修改控件内部状态时忘记调用setState,框架不会知道控件是dirty的,可能不会调用控件的build函数,这意味着用户界面不会更新以反映更改的状态。 通过这种方式管理动态,不再需要单独编写代码来创建和更新子控件。相反,只需要实现构建函数,即可处理这两种情况。 以下是最终效果图
|