Flutter-go 项目地址是:https://github.com/alibaba/flutter-go
上文 我们分析了 第四个 Tab 页面,主要分析了 翻页动画的实现
这篇文章主要拆解 详情页面和页面跳转。
下图是整理后的 详情页面和页面跳转 有关的内容:
ps: 这篇代码量有点多,看不懂代码可以先知道这个功能是如何去实现的,待后边动手实现项目的时候回继续补充讲解。
页面跳转
页面跳转使用的是fluro
库,fluro
库的使用只需以下两步即可。
- 先做实例化
final router = Router();
- 定义
handler
,用于处理路由的路径和参数的传递,如:
var usersHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
// UsersScreen 页面,接收参数为 id 的值
return UsersScreen(params["id"][0]);
});
void defineRoutes(Router router) {
// 下面的路由会拦截如: /users/1234,的页面路径
router.define("/users/:id", handler: usersHandler);
// 需要页面跳转动画可以使用 transitionType
// router.define("users/:id", handler: usersHandler, transitionType: TransitionType.inFromLeft);
}
下面来看一下如何使用Router
去进入Widget
详情页面。
- 在
main.dart
中实例化Router
,然后将对象赋值给application.dart
中的Router
对象,下次使用直接Application.router
即可调用 - 在
router_handler.dart
文件中声明了handler
,然后在routers.dart
文件中调用对应的handler
。如:
// 定义Handler
var categoryHandler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
// 获取参数
String name = params["type"]?.first;
return new CategoryHome(name);
},
);
// 定义路径
router.define('/category/:type', handler: categoryHandler);
// 页面跳转
Application.router.navigateTo(
context, "/category/${item.name}",
transition: TransitionType.inFromRight);
详情页面分析
分类页面
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
// 当前页面将退出,为了多层分类页面的复用
body: WillPopScope(
onWillPop: () {
return back();
},
child: ListView(
children: <Widget>[
_buildContent(),
],
),
// child: Container(color: Colors.blue,child: Text('123'),),
),
);
网格布局主要在 _buildContent
的WidgetItemContainer
控件中通过循环去实现。WidgetItemContainer
实现的思路如下:
伪代码如下:
List<Widget> _buildColumns(context) {
List<Widget> _listWidget = [];
List<Widget> _listRows = [];
int addI;
for (int i = 0, length = categories.length; i < length; i += columnCount) {
_listRows = [];
for (int innerI = 0; innerI < columnCount; innerI++) {
addI = innerI + i;
if (addI < length) {
dynamic item = categories[addI];
_listRows.add(
Expanded(
flex: 1,
child: WidgetItem(
title: item.name,
onTap: () {
if (isWidgetPoint) {
String targetName = item.name;
String targetRouter = '/category/error/404';
widgetDemosList.forEach((item) {
if (item.name == targetName) {
targetRouter = item.routerName;
}
});
Application.router.navigateTo(context, "$targetRouter", transition: TransitionType.inFromRight);
} else {
Application.router
.navigateTo(context, "/category/${item.name}", transition: TransitionType.inFromRight);
}
},
index: addI,
totalCount: length,
rowLength: columnCount,
textSize: isWidgetPoint ? 'middle' : 'small',
),
),
);
} else {
_listRows.add(
Expanded(
flex: 1,
child: Container(),
),
);
}
}
_listWidget.add(
Row(
children: _listRows,
),
);
}
return _listWidget;
}
Widget 详情页面
路径:
lib/componets/widget_demos.dart
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
new IconButton(
tooltip: 'goBack home',
onPressed: () {
Navigator.popUntil(context, ModalRoute.withName('/'));
},
icon: Icon(Icons.home),
),
new IconButton(
tooltip: 'collection',
onPressed: _getCollection,
icon: Icon(_collectionIcons),
),
PopupMenuButton<String>(
onSelected: _selectValue,
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
const PopupMenuItem<String>(
value: 'doc',
child: ListTile(
leading: Icon(
Icons.library_books,
size: 22.0,
),
title: Text('查看文档'),
),
),
const PopupMenuDivider(),
const PopupMenuItem<String>(
value: 'code',
child: ListTile(
leading: Icon(
Icons.code,
size: 22.0,
),
title: Text('查看Demo'),
),
),
],
),
],
),
body: Container(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
child: ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(0.0),
children: <Widget>[
Column(
children: _buildContent(),
),
],
),
),
bottomNavigationBar:
(widget.bottomNaviBar is Widget) ? widget.bottomNaviBar : null);
}
从上面的源码可以看出 点击图标回首页 的操作是:
Navigator.popUntil(context, ModalRoute.withName('/'));
PopupMenu 弹窗的实现也是很简单,直接使用PopupMenuButton
即可生成。
中间的控件描述是通过获取具体的Widget
去展示的,在展示的时候用到了Markdown
库flutter_markdown
。
例如:展示Appbar
组件的介绍,在widgets/components/Bar/AppBar
路径下有个index.dart
,在index.dart
里面使用markdown
格式返回了WidgetDemo
控件要显示的内容(也就是展示的详情页)
那么那么多个widget
它是如何去区分点击的是那个呢?
void onWidgetTap(WidgetPoint widgetPoint) {
String targetName = widgetPoint.name;
String targetRouter = '/category/error/404';
widgetDemosList.forEach((item) {
// print("targetRouter = item.routerName> ${[item.name,targetName]}");
if (item.name == targetName) {
targetRouter = item.routerName;
}
});
Application.router.navigateTo(context, "$targetRouter");
}
通过点击事件可以看出,页面跳转的时候传递了widget
的名称,所以它是通过名称来区别widget
的,在widget_demo.dart
中通过便利所有widget
找到该名称的widget
然后用MarkDownBody
去展示。伪代码如下:
// 传递 title
class _DemoState extends State<Demo> {
@override
Widget build(BuildContext context) {
return WidgetDemo(
title: 'AppBar',
codeUrl: 'components/Bar/AppBar/demo.dart',
contentList: allDomes(context, this),
docUrl: 'https://docs.flutter.io/flutter/material/AppBar-class.html',
);
}
}
// 得到 title,遍历获取该 widget 并展示
List<Widget> _buildContent() {
List<Widget> _list = [
SizedBox(
height: 10.0,
),
];
widget.contentList.forEach((item) {
if (item.runtimeType == String) {
_list.add(MarkdownBody(item));
_list.add(
SizedBox(
height: 20.0,
),
);
} else {
_list.add(item);
}
});
return _list;
}
为了适配底部导航栏Widget
还添加了bottomNavigationBar
去控制显示导航栏。
web 页面
接收
url
和title
参数 ,使用了WebviewScaffold
控件,这是一个第三方的库(flutter_webview_plugin
)提供的控件,该库使用文档
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
new IconButton(
tooltip: 'goBack home',
onPressed: _getCollection,
icon: Icon(
_collectionIcons,
),
),
],
),
body: WebviewScaffold(
url: widget.url,
withZoom: false,
withLocalStorage: true,
withJavascript: true,
),
);
调用本地浏览器打开网页
使用了
url_launcher
库,该库允许您打开移动平台上的默认浏览器来显示给定的URL
,它在Android
和iOS
上均受支持。
// 点击首页的 Banner 启动手机浏览器
void _launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
// 调用
if (arr.length > 0) {
list.add(HomeBanner(bannerStories, (story) {
_launchURL('${story.url}');
}));
}
代码阅读页面
使用了
RichText
控件,同时使用了代码高亮工具类SyntaxHighlighterStyle
去高亮代码。
Widget build(BuildContext context) {
// 定义好样式
final SyntaxHighlighterStyle style =
Theme.of(context).brightness == Brightness.dark
? SyntaxHighlighterStyle.darkThemeStyle()
: SyntaxHighlighterStyle.lightThemeStyle();
Widget body;
if (_exampleCode == null) {
body = const Center(child: CircularProgressIndicator());
} else {
Widget _codeWidget;
try{
// 使用代码高亮
DartSyntaxHighlighter(style).format(_exampleCode);
// 富文本控件展示
_codeWidget = RichText(
text: TextSpan(
style: const TextStyle(fontFamily: 'monospace', fontSize: 10.0),
children: <TextSpan>[
DartSyntaxHighlighter(style).format(_exampleCode)
],),
);
}catch (err){
_codeWidget = Text(_exampleCode);
}
body = SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: _codeWidget,
),
);
}
点击按钮 Tab 页面切换
/// 第四个页面点击 回到首页 按钮
_goHomePage(context) {
Navigator.of(context)
.pushNamedAndRemoveUntil('/home', (Route<dynamic> route) => false);
}
本篇完~
网友评论