flutter项目实战
学习完基础知识之后,通过一个旅游的demo app来实践flutter的开发技能
项目代码地址:https://github.com/shengshunyan/my_flutter_app.git
项目结构
images/ 存放项目图片
pubspec.yaml 项目配置文件(依赖保,静态资源配置等)
lib/ 项目代码
- model/ 接口的数据模型
- dao/ 获取接口数据的服务层
- pages/ 页面模块
- widget/ 公共组件
- util/ 公共方法
- navigator/ 导航
- main.dart 主入口文件
导航
- 用PageView组件来控制页面模块的切换,PageView组件有相关的优化
dart
1 | Scaffold( |
首页
MediaQuery.removePadding可用于移除ListView顶部预设的padding
dart1
2
3
4
5MediaQuery.removePadding(
context: context,
removeTop: true,
child: ...
}RefreshIndicator组件可实现下拉刷新功能
dart1
2
3
4RefreshIndicator(
onRefresh: loadData,
child: ...
)NotificationListener组件可以监听页面的上下滚动
dart1
2
3
4
5
6NotificationListener(
onNotification: (scrollNotification) {
// ...
},
child: ...,
)ListView组件可以用于一个从上到下的模块内容展示
dart1
2
3
4
5
6ListView(
children: <Widget>[
widget_1,
widget_2,
]
)页面的跳转
dart1
2
3Navigator.push(context, MaterialPageRoute(builder: (context) {
return SpeakPage();
}));根据loading状态,展示页面加载中样式
dart1
2
3
4
5
6
7
8
9
10
Widget build(BuildContext context) {
if (isLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
return child;
}GestureDetector组件用于监听用户行为,比如点击
dart1
2
3
4
5GestureDetector(
onTap: () {
// ...
},
child: ...Row用于横向展示元素,Column用于竖向展示元素(很常用)
实现了一个WebView组件,用于app直接跳转h5页面,可以设置头部
dart1
2
3
4
5
6WebView(
url: model.url,
statusBarColor: model.statusBarColor,
title: model.title,
hideAppBar: model.hideAppBar,
));
搜索及语音页
- 继承自AnimatedWidget组件,实现了一个语音按钮的动画组件AnimatedMicdart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30const double MIC_SIZE = 80;
class AnimatedMic extends AnimatedWidget {
static final _opacityTween = Tween<double>(begin: 1, end: 0.5);
static final _sizeTween = Tween<double>(begin: MIC_SIZE, end: MIC_SIZE - 20);
AnimatedMic({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return Opacity(
opacity: _opacityTween.evaluate(animation),
child: Container(
height: _sizeTween.evaluate(animation),
width: _sizeTween.evaluate(animation),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(MIC_SIZE / 2),
),
child: Icon(
Icons.mic,
color: Colors.white,
size: 30,
),
),
);
}
}
旅拍
利用TabBar组件实现一个头部横向的tab切换功能
dart1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17TabBar(
controller: _controller,
isScrollable: true,
labelColor: Colors.black,
labelPadding: EdgeInsets.fromLTRB(20, 0, 10, 5),
indicator: UnderlineTabIndicator(
borderSide: BorderSide(
color: Color(0xff2fcfbb),
width: 3,
),
insets: EdgeInsets.only(bottom: 10)),
tabs: tabs.map<Tab>((TravelTab tab) {
return Tab(
text: tab.labelName,
);
}).toList()
)没有预设宽度/高度时,Flexible组件可以使Row、Column、Flex等子组件在主轴方向有填充可用空间的能力,但是不强制子组件填充可用空间。
dart1
2
3Flexible(
child: ...
)flutter_staggered_grid_view库的组件StaggeredGridView可用于grid布局的实现(瀑布流);还可利用controller属性监听上拉事件来实现上拉到底部加载更多;mixin AutomaticKeepAliveClientMixin组件来保持缓存各个tab页;
dart1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53class TravelTabPage extends StatefulWidget {
_TravelTabPageState createState() => _TravelTabPageState();
}
// 缓存各个tab页的内容(1) AutomaticKeepAliveClientMixin mixin
class _TravelTabPageState extends State<TravelTabPage>
with AutomaticKeepAliveClientMixin {
// 用于上拉加载更多
ScrollController _scrollController = ScrollController();
void initState() {
_loadData();
// 上拉加载更多
_scrollController.addListener(() {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
_loadData(loadMore: true);
}
});
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
body: StaggeredGridView.countBuilder(
controller: _scrollController,
crossAxisCount: 4,
itemCount: travelItems?.length ?? 0,
itemBuilder: (BuildContext context, int index) =>
_TravelItem(index: index, item: travelItems[index]),
staggeredTileBuilder: (int index) => new StaggeredTile.fit(2),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
);
}
// 获取页面数据
void _loadData({bool loadMore = false}) {
// ...
}
// 缓存各个tab页的内容(2)
bool get wantKeepAlive => true;
Future<Null> _handleRefresh() async {
_loadData();
return null;
}
}
我的
我的 模块页面直接引用的h5页面
dart
1 |
|