学习flutter除了官方文档之外,其官方本身的examples组织的也很好,算是学习的极好的资料。本篇博文就是摘取的其examples目录下的flutter_gallery项目进行研读和学习。本篇博文摘取了gallery下的调色版的代码进行分析解读,算是一个学习笔记。其代码被博主发挥了拿来注意的精神放在了自己的github上,对关键代码进行了注释。
运行效果如下所示:
整个页面分成两个部分:导航栏和对应导航tab下的ListView组成。功能也很简单,就是显示一些导航tab下的颜色值。
通过这个sample可以了解:
1)导航栏的简单使用
2)ListView的简单使用
3)MaterialColor的简单使用
4)flutter的整体项目架构
在开始之前,建议读者阅读Material Design的三类颜色,对primaryColor和accentColor有个基本的认知。在Flutter中用MaterialColor,MaterialAccentColor,Colors三个类来供我们使用。比如红色的primaryColor定义了如下代码:
static const MaterialColor red = MaterialColor(
_redPrimaryValue,
<int, Color>{
50: Color(0xFFFFEBEE),
100: Color(0xFFFFCDD2),
200: Color(0xFFEF9A9A),
300: Color(0xFFE57373),
400: Color(0xFFEF5350),
500: Color(_redPrimaryValue),
600: Color(0xFFE53935),
700: Color(0xFFD32F2F),
800: Color(0xFFC62828),
900: Color(0xFFB71C1C),
},
);
而红色的accentColor则表示如下:
static const MaterialAccentColor redAccent = MaterialAccentColor(
_redAccentValue,
<int, Color>{
100: Color(0xFFFF8A80),
200: Color(_redAccentValue),
400: Color(0xFFFF1744),
700: Color(0xFFD50000),
},
);
因为上面red和redAccent 两个静态变量都在Colors类里面定义,所以我们可以这么使用:
MaterialColor red = Colors.red;
MaterialColor red50=Collors.red[50];
MaterialAccentColor redAccent=Colors.redAccent;
MaterialAccentColor redAccent100=Colors.redAccent[100];
文章开头的图片中的listView的item展示的就是颜色primaryColor 50,100-900,accentColor 100,200,400,700的颜色值。
有上图所示,这个简单的demo显示了各个颜色的对应primaryColor和accentColor组成的列表。所以用一个调色板对象_Palette来表示(Palette前面的下滑线表明该类为私有,类似有private):
//Palette:调色板对象
class _Palette {
_Palette({ this.name, this.primary, this.accent});
final String name;
//primaryColor
final MaterialColor primary;
//accent color
final MaterialAccentColor accent;
}
并且初始化了一个数据集合:
其中_allPalettes的name用来构建导航栏的各个tab.
在Flutter的app中你能看到的任意一个元素都是一个Widget,那么我们怎么将_allPalettes集合中的数据转换成一个个Wiget呢?集合有一个map方法可以帮我们做一个映射。所以上图导航栏的各个tab标签就是这么生成的
tabs: _allPalettes.map<Widget>((_Palette palette) => Tab(text: palette.name)
).toList(),
上面一行代码就是将_allPalettes集合里面的数据遍历其name属性,然后生成一个widget集合,确切地说是Tab对象的集合(Tab也是个Widget),交给导航栏的tabs属性使用。
导航栏完整代码如下:
bottom: TabBar(
isScrollable: true,
//生成导航栏的每一个tab
tabs: _allPalettes.map<Widget>((_Palette palette) => Tab(text: palette.name)
).toList(),
),
),
同理,对应tab下的页面代码如下所示:
//导航栏各个tab对应的页面
body: TabBarView(
children: _allPalettes.map<Widget>((_Palette palette) {
return _PaletteTabView(colors: palette);
}).toList(),
),
),
注意,每个tab下对应的页面是这样一个Widget对象:它是一个自定义的Widget,名字叫_PaletteTabView,该Widget里面有一个ListView,用来渲染如上图所示的结果页面。
这样的话,点击各个tab,就会跳转到对应的_PaletteTabView,我们无需像Android那样设个OnSelectedListener或者onClickListener监听,Flutter帮我们做好了这样的工作。
上面说了_PaletteTabView返回的是一个ListView,其build方法如下:
return ListView(
//设置item的高度
itemExtent: _itemHeight,
//<Widget>[]数组
children: colorItems,
);
代码也很简单,我们通过itemExtent设置了item的高度;然后ListView的各个item通过children属性来配置,children接受的类型是一个Widget[],那么上面的colorItems是如何生成的呢?说这个问题之前,先看看其Item对象如下实现:
return Semantics(
container: true,
child: Container(
//因为在listView中已经设置了高度,所以这个高度相当于match_parent
height: _itemHeight,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
//背景色:展示primaryColor或者accentColor
color: color,
child: SafeArea(
top: false,
bottom: false,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
//上图每个item左边的文本信息
Text('$prefix$index'),
//上图每个item右边的颜色值信息
Text(colorString()),
],
),
),
),
);
那么ListView中对应的colorsItem生成的代码如下:
从上面的代码中也可以看出,listView的item可以是任意样式的item,比较灵活,而不像android的recyclerView那样需要设置不同的ViewHolder。
到此为止,本篇博文到此结束。如有不当之处欢迎批评指正。
代码传送门