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);
}
本篇完~