flutter - 01 基础介绍以及ListView
这篇主要讲flutter最基本的操作。我们从一个实例入手,先不需要知道它里面的每一行是什么意思,我会慢慢说。
main.dart
1 import 'package:flutter/material.dart'; 2 import 'model/post.dart'; 3 4 void main() => runApp(App()); 5 6 7 class App extends StatelessWidget { 8 @override 9 Widget build(BuildContext context) { 10 return MaterialApp( 11 home: Home(), 12 theme: ThemeData( 13 primarySwatch: Colors.red 14 ), 15 ); 16 } 17 } 18 19 class Home extends StatelessWidget { 20 Widget _listItemBuilder (BuildContext context, int index) { 21 return Container( 22 color: Colors.white, 23 margin: EdgeInsets.all(8.0), 24 child: Column( 25 children: <Widget>[ 26 Image.network(posts[index].imageUrl), 27 SizedBox(height: 16.0), 28 Text( 29 posts[index].title, 30 style: Theme.of(context).textTheme.title, 31 ), 32 Text( 33 posts[index].author, 34 style: Theme.of(context).textTheme.subhead, 35 ), 36 SizedBox(height: 8.0), 37 ], 38 ), 39 ); 40 } 41 42 @override 43 Widget build(BuildContext context) { 44 // TODO: implement build 45 return Scaffold( 46 backgroundColor: Colors.grey[200], 47 appBar: AppBar( 48 title: Text('Hello'), 49 elevation: 4.0, 50 ), 51 body: ListView.builder( 52 itemCount: posts.length, 53 itemBuilder: _listItemBuilder, 54 ), 55 ); 56 } 57 } 58 59 class Hello extends StatelessWidget { 60 @override 61 Widget build(BuildContext context) { 62 return Center( 63 child: Text( 64 'hello liwenchi', 65 textDirection: TextDirection.ltr, 66 style: TextStyle( 67 fontSize: 30, 68 color: Colors.blue 69 ), 70 ) 71 ); 72 } 73 }
第一行是指引入flutter自带的material风格的组件库。
第二行是指引入了一个自己定义的文件,同级目录下的model文件夹的post.dart,是自己创建的,里面有一些post,以便练习ViewList。
post.dart
1 import 'package:english_words/english_words.dart'; 2 3 class Post { 4 const Post({ 5 this.title, 6 this.author, 7 this.imageUrl 8 }); 9 10 final title; 11 final author; 12 final imageUrl; 13 } 14 15 var wordPair = new WordPair.random(); 16 17 final List<Post> posts = [ 18 Post ( 19 title: wordPair.asPascalCase, 20 author: wordPair.asPascalCase, 21 imageUrl: 'https://img1.gamersky.com/image2019/04/20190420_ljt_red_220_4/gamersky_020small_040_201942016592D9.jpg' 22 ), 23 Post ( 24 title: wordPair.asPascalCase, 25 author: wordPair.asPascalCase, 26 imageUrl: 'https://img1.gamersky.com/image2019/04/20190420_ljt_red_220_4/gamersky_019origin_037_2019420165980D.jpg' 27 ), 28 Post ( 29 title: wordPair.asPascalCase, 30 author: wordPair.asPascalCase, 31 imageUrl: 'https://img1.gamersky.com/image2019/04/20190420_ljt_red_220_4/gamersky_002small_004_201942016591DC.jpg' 32 ), 33 Post ( 34 title: wordPair.asPascalCase, 35 author: wordPair.asPascalCase, 36 imageUrl: 'https://img1.gamersky.com/image2019/04/20190420_ljt_red_220_4/gamersky_010origin_019_20194201659245.jpg' 37 ) 38 ];
void main() => runApp(App());
这里是main函数的箭头函数写法,runApp()的参数是一个widget,例如,这里的参数是App类的实例,因此可以推断,App类是Widget类的继承。
widget可以翻译为组件、部件,整个flutter应用就是由一个个widget组成的。
当一个类继承自widget部件的时候,要重写它的build方法,而这个build方法的返回值也是一个widget。就像上面的App类一样。
1 class App extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 return MaterialApp( 5 home: Home(), 6 theme: ThemeData( 7 primarySwatch: Colors.red 8 ), 9 ); 10 } 11 }
因此,可以推断出,MaterialApp也是一个widget。但是我们可以看到,代码段中并没有定义MaterialApp类,因此可以推断出,他是package:flutter/material.dart中导入的widget。
根据我目前的学习,widget分为两种,StatelessWidget和StatefulWidget,大概是说,前者是无状态的(静态的,内容不会发生改变),而后者是有状态的,内容可以动态变化。
ListView
在本例的Home部件中,返回了一个Scaffold部件作为MaterialApp的home属性的值。其中,设置了backgroundColor属性为灰色和appBar,一个内容为Hello的导航栏,且导航栏的阴影大小为4.0,如果想设计成扁平化风格,还可以把elevation的值设为0。
1 @override 2 Widget build(BuildContext context) { 3 // TODO: implement build 4 return Scaffold( 5 backgroundColor: Colors.grey[200], 6 appBar: AppBar( 7 title: Text('Hello'), 8 elevation: 4.0, 9 ), 10 body: ListView.builder( 11 itemCount: posts.length, 12 itemBuilder: _listItemBuilder, 13 ), 14 ); 15 }
其中主体部分是一个列表,在这里用的两个值 itemCount和 itemBuilder。
这里的 itemCount是这个 ListView的长度,且要小于等于真实的数量,否则会以找不到索引而报错。
itemBuilder的值应该是一个返回值是 widget的函数,在本例中是一个 container,一个 container包含一张图片,两行文字,和两个空高度作为分割文字的行高来使用。
1 Widget _listItemBuilder (BuildContext context, int index) { 2 return Container( 3 color: Colors.white, 4 margin: EdgeInsets.all(8.0), 5 child: Column( 6 children: <Widget>[ 7 Image.network(posts[index].imageUrl), 8 SizedBox(height: 16.0), 9 Text( 10 posts[index].title, 11 style: Theme.of(context).textTheme.title, 12 ), 13 Text( 14 posts[index].author, 15 style: Theme.of(context).textTheme.subhead, 16 ), 17 SizedBox(height: 8.0), 18 ], 19 ), 20 ); 21 }
注意,这个函数的第二个参数index,是从0迭代到 itemCount - 1。