新贵 Flutter (6)中 Material Design
2019-06-19 本文已影响43人
zidea

大家都知道 google 推出 Material Design 让没有多少设计经验的开发者也可以设计出精美界面。而且对 material design google 也投入了很多,轻松找到许多 iso 或 android 端的组件。当然 Flutter 官方也提供许多支持 Material Design 的组件。
这些组件我们轻松地在 Flutter 官网提供 Material Component Widgets 找到。
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget{
const MyApp();
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(home: const MyHomePage(),);
}
}
class MyHomePage extends StatelessWidget{
const MyHomePage({Key key}):super(key:key);
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container();
}
}
创建课件数据类,用于持有数据,Tut 包含课件名称、说明和课时以及图片。
class Tut{
const Tut({this.name, this.description,this.courses,this.imageUrl});
final String name;
final String description;
final String courses;
final String imageUrl;
}
这里模拟数据集合,这一切都是为了演示做基础,为一些假数据。这里简单用 express 启动一个服务提供图片,供 flutter 对图片进行预览。
final List<Tut> _tuts = <Tut>[
Tut(
name: 'React',
description: '有关 React 的基础教程',
courses: 12,
imageUrl: 'http://$server:3000/images/react.png'),
Tut(
name: 'Vue',
description: '有关 Vue 的基础教程',
courses: 12,
imageUrl: 'http://$server:3000/images/vue.jpeg'),
Tut(
name: 'Angular',
description: '有关 Angular 的基础教程',
courses: 6,
imageUrl: 'http://$server:3000/images/angular.jpg')
];
创建 ListView 的视图列表,调用其静态方法 builder 来构建 ListView,在 builder 方法传入 itemCount 也就是条目数,和 itemExtent 是列表条目的高度,itemBuilder 接受一个函数作为参数,函数会得到 context 和 index(位置信息)的参数来构建列表内容,返回 Widget 就是列表条目的内容。
class MyHomePage extends StatelessWidget{
const MyHomePage({Key key}):super(key:key);
Widget _listItemBuilder(BuildContext context, int index){
return Text(_tuts[index].name);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(title: Text('Tuts'),),
body: ListView.builder(itemCount: _tuts.length,itemExtent:60.0,itemBuilder: _listItemBuilder),
);
}
}

Widget _listItemBuilder(BuildContext context, int index){
return Text(_tuts[index].name,style: Theme.of(context).textTheme.headline,);
}
-
Theme.of(context).textTheme.headline
获取主题,然后为文本添加主题默认文本样式。
Widget _listItemBuilder(BuildContext context, int index){
return new Container(
padding: const EdgeInsets.only(left: 16.0),
child: Text(_tuts[index].name,style: Theme.of(context).textTheme.headline,),
) ;
}
-
padding: const EdgeInsets.only(left: 16.0),
通过 padding 属性添加内边距属性。EdgeInsets 类的 only 方法来控制左侧的内边距。
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
Widget _listItemBuilder(BuildContext context, int index) {
return new GestureDetector(
onTap: () => showDialog(
context: context,
builder: (context) => _dialogBuilder(context, _tuts[index])),
child: Container(
padding: const EdgeInsets.only(left: 16.0),
child: Text(
_tuts[index].name,
style: Theme.of(context).textTheme.headline,
),
),
);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Tuts'),
),
body: ListView.builder(
itemCount: _tuts.length,
itemExtent: 60.0,
itemBuilder: _listItemBuilder),
);
}
Widget _dialogBuilder(BuildContext context, Tut tut) {
return SimpleDialog(
children: <Widget>[
Container(
width: 80.0,
height: 80.0,
)
],
);
}
}
- 使用手势组件对条目组件进行包裹,从而为条目添加点击事件
onTap
点击条目后会弹出一个对话框,与上面创建条目一样我们需要为函数做 dialog 构建函数,该函数返回组件就是我们看到对话框 SimpleDialog 。
图
- 为 SimpleDialog 添加内容,这里添加一个图片 Image ,然后调用 network 方法来从网上获取图片资源将其显示,通过将 BoxFit.fill 传入到 fit 参数来用图片将对话框撑起来。
Widget _dialogBuilder(BuildContext context, Tut tut) {
return SimpleDialog(
children: <Widget>[
Image.network(tut.imageUrl, fit:BoxFit.fill,),
],
);
}

- 接下来将课程的一些信息如名称、说明显示在图片下方。
Widget _dialogBuilder(BuildContext context, Tut tut) {
return SimpleDialog(
contentPadding: EdgeInsets.zero,
children: <Widget>[
Image.network(tut.imageUrl, fit:BoxFit.fill,),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[],
),
)
],
);
}

Widget _dialogBuilder(BuildContext context, Tut tut) {
return SimpleDialog(
contentPadding: EdgeInsets.zero,
children: <Widget>[
Image.network(tut.imageUrl, fit:BoxFit.fill,),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Text(tut.name),
Text('${tut.courses} 课'),
Text(tut.description)
],
),
)
],
);
}

Widget _dialogBuilder(BuildContext context, Tut tut) {
return SimpleDialog(
contentPadding: EdgeInsets.zero,
children: <Widget>[
Image.network(tut.imageUrl, fit:BoxFit.fill,),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(tut.name),
Text('${tut.courses} 课'),
Text(tut.description)
],
),
)
],
);
}

Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(tut.name, style: localTheme.textTheme.display1,),
Text('${tut.courses} 课',style: localTheme.textTheme.subhead.copyWith(fontStyle: FontStyle.italic),),
SizedBox(height: 16.0,),
Text(tut.description)
],
),
)
- style 使用 localTheme 为字体添加显示样式
-
SizeBox 可以为文字添加行间距
图
完整代码
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp();
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.purple,buttonColor: Colors.purple,buttonTheme: const ButtonThemeData(
textTheme: ButtonTextTheme.primary,
)),
home: const MyHomePage(),
);
}
}
class Tut {
const Tut({this.name, this.description, this.courses, this.imageUrl});
final String name;
final String description;
final int courses;
final String imageUrl;
}
final String server =
defaultTargetPlatform == TargetPlatform.android ? "10.0.2.2" : "localhost";
final List<Tut> _tuts = <Tut>[
Tut(
name: 'React',
description: '有关 React 的基础教程',
courses: 12,
imageUrl: 'http://$server:3000/images/react.png'),
Tut(
name: 'Vue',
description: '有关 Vue 的基础教程',
courses: 12,
imageUrl: 'http://$server:3000/images/vue.jpeg'),
Tut(
name: 'Angular',
description: '有关 Angular 的基础教程',
courses: 6,
imageUrl: 'http://$server:3000/images/angular.jpg')
];
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
Widget _listItemBuilder(BuildContext context, int index) {
return new GestureDetector(
onTap: () => showDialog(
context: context,
builder: (context) => _dialogBuilder(context, _tuts[index])),
child: Container(
padding: const EdgeInsets.only(left: 16.0),
child: Text(
_tuts[index].name,
style: Theme.of(context).textTheme.headline,
),
),
);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Tuts'),
),
body: ListView.builder(
itemCount: _tuts.length,
itemExtent: 60.0,
itemBuilder: _listItemBuilder),
);
}
Widget _dialogBuilder(BuildContext context, Tut tut) {
ThemeData localTheme = Theme.of(context);
return SimpleDialog(
contentPadding: EdgeInsets.zero,
children: <Widget>[
Image.network(tut.imageUrl, fit:BoxFit.fill,),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(tut.name, style: localTheme.textTheme.display1,),
Text('${tut.courses} 课',style: localTheme.textTheme.subhead.copyWith(fontStyle: FontStyle.italic),),
SizedBox(height: 16.0,),
Text(tut.description,style: localTheme.textTheme.body1,),
SizedBox(height: 16.0,),
Align(
alignment: Alignment.centerRight,
child: Wrap(
children: <Widget>[
FlatButton(
onPressed: (){
Navigator.of(context).pop();
},
child: const Text('更多信息'),
),
RaisedButton(
onPressed: (){},
child: const Text('按钮'),
)
],
),
)
],
),
)
],
);
}
}
- 继续美化界面,在文字下方添加一些按钮表示一些功能,
- Align 组件用于按钮组件排列方向,
alignment: Alignment.centerRight
表示居中靠右,然后添加两个按钮分别是 FlatButton 和 RaiseButton
图