1. 源碼下載
喜歡的話,別忘了點個關注,還有給個 Github 右上角的小星星吧。
源碼下載地址,代碼會根據不斷更新。
Flutter 仿生微信(目錄)
上一篇:Flutter 仿生微信(2):Pages 創建
下一篇:Flutter 仿生微信(4):我的頁面搭建
2. 思路
發現頁是幾個頁面中最簡單的,靜態頁面,所以先從這里入手。
2.1 使用 ListView 完成
本來是準備使用 Scaffold 的 appBar 來實現導航條,然后 FMFind.dart 中使用最方便的 ListView 即可完成。但是考慮到不同頁面導航條不同,狀態管理比較麻煩,所以放棄這個方案。
2.2 使用 CustomScrollView 完成
- CustomScrollView 可以自定義導航條,看了一下和預期比較接近。
- 然后就是結構了,頁面布局可以很清晰的看到,是一個常用的功能條和分割條,并且可以編輯展示在這里的功能條數量。
- 使用 Models 管理,只需要針對 Models 進行順序排列即可,直觀便捷。比如我們先放個 Model(朋友圈),在放一個 Model(分隔行),再放一個 Model(掃一掃),就完成了 朋友圈,分隔,掃一掃這種功能。
Models -> [model1, model2..], [model_divider], [model3, model4..].. -> 分類布局完成后 -> <Widget>[Widget(model1), Widget(model2), Widget(model_divider), Widget(model3)....] -> 刷新頁面。
FM Weixin Find.png
FM Weixin Find.gif
3. 示例代碼
FMFind.dart
import 'package:FMWeixinApp/find/item/FMFindItem.dart';
import 'package:FMWeixinApp/find/model/FMFindModel.dart';
import 'package:FMWeixinApp/tools/FMColor.dart';
import 'package:flutter/material.dart';
class FMFind extends StatefulWidget {
@override
FMFindState createState()=> FMFindState();
}
class FMFindState extends State <FMFind> {
List<Widget> _slivers = [];
List <FMFindModel> _models = [];
List <FMFindMenuModel> _menuModels = [];
@override
void initState() {
// TODO: implement initState
_models.clear();
_initModels();
_initMenuModels(_models);
_initSliversWithModels(_models);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return CustomScrollView(
slivers: _slivers,
);
}
SliverAppBar _sliverAppBar(){
return SliverAppBar(
title: Text('發現',
style: TextStyle(fontSize: 20),
),
backgroundColor: FMColors.wx_gray,
floating: true,
pinned: true,
elevation: 0.0,
);
}
// 功能 Items
SliverFixedExtentList _sliverFixedExtentList(FMFindMenuModel menuModel){
return SliverFixedExtentList(
delegate: SliverChildBuilderDelegate(
(context, index) => FMFindItem(
model: menuModel.models[index],
onTap: (model){
},
),
childCount: menuModel.models.length,
),
itemExtent: 60.0,
);
}
// 空白 blank
SliverFixedExtentList _sliverDividList(FMFindMenuModel menuModel){
return SliverFixedExtentList(
delegate: SliverChildBuilderDelegate(
(context, index) => Padding(padding: EdgeInsets.zero),
childCount: menuModel.models.length,
),
itemExtent: 10.0,
);
}
void _initModels(){
_models.add(FMFindModel('assets/images/find/find_friend.png', '朋友圈', 'function'));
_models.add(FMFindModel('', '', 'divid'));
_models.add(FMFindModel('assets/images/find/find_scan.png', '掃一掃', ''));
_models.add(FMFindModel('', '', 'divid'));
_models.add(FMFindModel('assets/images/find/find_look.png', '看一看', ''));
_models.add(FMFindModel('assets/images/find/find_search.png', '搜一搜', ''));
_models.add(FMFindModel('', '', 'divid'));
_models.add(FMFindModel('assets/images/find/find_game.png', '游戲', ''));
_models.add(FMFindModel('', '', 'divid'));
_models.add(FMFindModel('assets/images/find/find_small_project.png', '小程序', ''));
}
void _initSliversWithModels(models){
_slivers.add(_sliverAppBar());
_menuModels.forEach((menuModel) {
if (menuModel.dividModel) {
_slivers.add(_sliverDividList(menuModel));
} else {
_slivers.add(_sliverFixedExtentList(menuModel));
}
});
}
void _initMenuModels(List <FMFindModel> items){
List <FMFindModel> _tempModels = [];
items.forEach((model) {
if (model.type == 'divid') {
_menuModels.add(new FMFindMenuModel(_tempModels));
_tempModels.clear();
_tempModels.add(model);
FMFindMenuModel menuModel = new FMFindMenuModel(_tempModels);
menuModel.dividModel = true;
_menuModels.add(menuModel);
_tempModels.clear();
} else {
_tempModels.add(model);
}
});
if (_tempModels.length > 0) {
_menuModels.add(new FMFindMenuModel(_tempModels));
_tempModels.clear();
}
}
}
FMFindModel.dart
class FMFindModel {
String imageName;
String title;
// 分割線
String hasDivid;
// type
String type;
FMFindModel(this.imageName, this.title, this.type);
}
class FMFindMenuModel {
// 是否為分隔單位
bool dividModel = false;
//
List <FMFindModel> _models = [];
List <FMFindModel> get models => _models;
FMFindMenuModel(List <FMFindModel> models) {
_models.clear();
models.forEach((model) {
_models.add(model);
});
}
}
FMFindItem.dart
import 'package:FMWeixinApp/find/model/FMFindModel.dart';
import 'package:FMWeixinApp/tools/FMColor.dart';
import 'package:flutter/material.dart';
class FMFindItem extends StatefulWidget {
final FMFindModel model;
final void Function(FMFindModel model) onTap;
const FMFindItem({
Key key,
this.model,
this.onTap,
}):super(key: key);
@override
FMFindItemState createState()=> FMFindItemState();
}
class FMFindItemState extends State <FMFindItem> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return SizedBox(
child: GestureDetector(
onTap: (){
if (widget.onTap != null) widget.onTap(widget.model);
},
child: _stack(),
),
);
}
Stack _stack(){
return Stack(
children: [
Positioned(
left: 0,
right: 0,
top: 0,
bottom: 0,
child: _container(),
),
Positioned(
bottom: 0,
height: 1,
left: 60,
right: 0,
child: Divider(color: FMColors.wx_gray, thickness: 1,),
),
],
);
}
Container _container(){
return Container(
child: Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: _row(),
),
color: Colors.white,
);
}
Row _row(){
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SizedBox(
width: 30,
height: 30,
child: Image(image: AssetImage('${widget.model.imageName}')),
),
Padding(padding: EdgeInsets.all(8)),
Text('${widget.model.title}',
style: TextStyle(fontSize: 17),
),
],
),
Expanded(child: Padding(padding: EdgeInsets.zero)),
SizedBox(
width: 12,
height: 30,
child: Image(image: AssetImage('assets/images/find/find_arrow_right.png')),
),
],
);
}
}
4. 源碼分析
4.1 導航條實現
CustomScrollView.silvers 的第一個元素使用 SliverAppBar,自定義導航條。
4.2 Models 管理
- 為什么要使用 Models 進行管理?
- 后續這里可能是網絡讀取數據,json -> Models -> 刷新 View。
- 使用 Models 管理,只需要針對 Models 進行順序排列即可,直觀便捷。比如我們先放個 Model(朋友圈),在放一個 Model(分隔行),再放一個 Model(掃一掃),就完成了 朋友圈,分隔,掃一掃這種功能。
- Models 管理的實現
- 首先,我們創建一個分組 List <FMFindMenuModel> _menuModels,以及創建一個 List <FMFindModel> _models。
- _models 中我們就放 朋友圈、分隔、掃一掃、空格、、、等 FMFindModel 的集合,順序按照我們需要的順序排版
- 遍歷 _models,按照 分隔 Model 來對 _models 進行分組,每一組都創建一個 FMFindMenuModel,并將拆分出來的 FMFindModel 分組,保存到 FMFindMenuModel.models 中。
- 遍歷 _menuModels,按照對應分組開始創建 SliverFixedExtentList,并區分是功能性 Model 還是 分隔性 Model,生成兩種不同的 SliverFixedExtentList。
- 將 SliverAppBar,SliverFixedExtentList 整合到一起,提供給 CustomScrollView.silvers 完成我們想要的頁面。
FM Weixin Find.png
FM Weixin Find.gif