flutter_web 实战之文章列表与详情
在上一篇 【flutter_web 初體驗】中,簡單的介紹了下 flutter_web 創(chuàng)建一個項目和一些踩坑的解決方案,本篇將進一步講解搭建 flutter_web 項目的基本過程。
本文將講解以下要點:
項目目錄結(jié)構(gòu)
/ ├── README.md ├── analysis_options.yaml ├── build # webdev build 編譯生成的目錄,用于部署 ├── lib # 工作區(qū) │?? ├── components # 組件(Widgets) │?? ├── kit # 工具、父類 │?? ├── main.dart # 入口 │?? ├── models # 數(shù)據(jù)模型 │?? ├── network # 網(wǎng)絡(luò) │?? ├── pages # 頁面 │?? │?? ├── detail # 詳情頁 │?? │?? │?? ├── bloc │?? │?? │?? ├── detail.dart │?? │?? │?? ├── model │?? │?? │?? └── page │?? │?? ├── index # 首頁 │?? │?? ├── pages.dart │?? │?? └── user # 用戶 │?? └── router # 頁面路由 ├── pubspec.lock ├── pubspec.yaml # 依賴 └── web├── assets # 資源區(qū)│?? ├── FontManifest.json # 字體│?? └── images # 圖片│?? └── swift_logo.png├── index.html└── main.dart 復(fù)制代碼主要劃分了 6 大部分:
- network: 網(wǎng)絡(luò)
- models: 模型
- router: 路由
- pages: 頁面
- components:組件
- kit: 工具、常量、基類等
布局
接下來,將要實現(xiàn) 2 個頁面, 效果分別如下:
列表和詳情,頁面還是比較簡單的。
整體布局就是頭部、內(nèi)容、尾部。比如尾部在首頁和詳情頁的底部都是一樣的,把它領(lǐng)出來作為一個公共組件。個人的開發(fā)習(xí)慣就是,相同的東西往上冒泡,讓文件目錄層次上浮。
首頁布局
因為頁面不存在懸浮情況,所以首頁布局還是比較簡單的:
// pages/index/index_pages.dart// 添加頭部 List<Widget> lists = [_buildHeader(context)];// 添加列表內(nèi)容 lists.addAll(rows.map((item) {return _buildCell(item); }).toList());// 添加尾部 lists.add(Container(margin: EdgeInsets.only(top: 100),child: FooterView(), ));/// 整體內(nèi)容用 SingleChildScrollView 進行包裝 SingleChildScrollView(child: Container(color: Colors.white,child: Column(children: lists,),)) 復(fù)制代碼詳情頁布局
詳情頁頭部是懸浮的,且文章采用 markdown,也是一個 ListView。 然后底部是通用的底部欄。
那么懸浮的話就采用了 AppBar :
Scaffold(backgroundColor: Colors.white,appBar: PreferredSize(child: HeaderView(), preferredSize: Size.fromHeight(50)),body: body) 復(fù)制代碼上面的 body 是通過 SingleChildScrollView 包裝:
return SingleChildScrollView(child: Column(children: <Widget>[mdView, FooterView()],), ); 復(fù)制代碼響應(yīng)式布局
在有使用過站點的初版的時候,當(dāng)你改變?yōu)g覽器的大小的時候,布局會比較丑陋,且會發(fā)送一些布局警告。造成的原因是沒有適配各種屏幕的大小。如果做前端的,會有一些比較通用的解決辦法:
- 媒體查詢
- 百分比
- rem
- vw/vh
如果對此感興趣可深入閱讀 《響應(yīng)式布局的常用解決方案對比(媒體查詢、百分比、rem和vw/vh)》
那么如果 flutter_web 要做響應(yīng)是布局,該怎么辦?
- 尺寸大小和位置不使用硬編碼
- 使用 MediaQuery 獲取當(dāng)前的窗口的大小
- 使用 Flexible 和 Expanded 去布局界面,使用百分比而不是硬編碼。
- 使用 LayoutBuilder 獲取父 widget 的 ConstraintBox
- 使用 MediaQuery 或 OrientationBuilder 獲取設(shè)備的方向。
- AspectRatio 和 FractionallySizedBox 是常用的百分比相關(guān)的 Widget。
響應(yīng)式布局有兩篇文章推薦閱讀:
- Build Responsive UIs in Flutter
- Making Cross-platform Flutter Landing Page Responsive (Part 3)
頁面路由
// main.dart main() {Static.storage = Storage();runApp(SwiftClub()); }class SwiftClub extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(debugShowCheckedModeBanner: false,title: 'swiftclub',theme: ThemeData(fontFamily: "Montserrat"),onGenerateRoute: (setting) => buildRouters(setting),initialRoute: "/",);} } 復(fù)制代碼// router/router.dartRoute<dynamic> buildRouters(RouteSettings settings) {dynamic args = settings.arguments;switch (settings.name) {case "/login":return SimpleRoute(name: "/login", title: "login", builder: (context) => LoginPage());case "/detail":if (args == null) {// 直接刷新當(dāng)前,參數(shù)可能不存在,返回首頁return defaultRoute();}// 解析頁面?zhèn)鬟f的參數(shù)final topicId = SafeValue.toInt(args['topicId']);return SimpleRoute(name: "detail",title: "detaila",builder: (context) => DetailPage(topicId: topicId,));case "/":return defaultRoute();default:return defaultRoute();} }SimpleRoute defaultRoute() {return SimpleRoute(name: '/', title: 'swiftclub', builder: (context) => IndexPage()); } 復(fù)制代碼flutter_web 的路由,跟 flutter 的路由管理是一樣的,主要是注意兩點:
-  如果刷新當(dāng)前頁面,之前其他頁面?zhèn)鬟f過來的參數(shù)就沒有了,刷新后,頁面獲取不到傳遞過來的參數(shù),進行網(wǎng)絡(luò)請求,報錯。所以這里做了判斷,如果參數(shù)不存在了,則返回到默認的首頁。 
-  路由參數(shù)取值,嘗試多次,發(fā)現(xiàn) ModalRoute.of(context).settings.arguments; 復(fù)制代碼
獲取不到 arguments,但在判斷路由的時候可以獲取。
網(wǎng)絡(luò)請求
由于 dart:io 在 flutter_web 中還不支持,所以 dio 是不能使用的,官方建議使用 package:http
Flutter for web: Frequently Asked Questions
為此做了個 http 請求的封裝,可參考使用:
import 'dart:convert'; import 'package:flutter_web/widgets.dart'; import 'package:http/http.dart' as http; import 'package:swiftclub/kit/macro/macro.dart';class Network {static getReq(String url, {Map params, Map headers}) async {var fullUrl = Macro.URL_base + url;return await _getReq(fullUrl, params: params, headers: headers);}static _getReq(String url, {Map params, Map headers}) async {var reqUri = _uriWith(url, queryParameters: params);http.Response response = await http.get(reqUri, headers: headers);var responseBody = json.decode(response.body);return responseBody;}static _postReq(String url, {Map headers, Map params}) async {var fullUrl = Macro.URL_base + url;if (headers != null && headers.isNotEmpty) {http.Response response =await http.post(Uri.parse(fullUrl), headers: headers, body: params);var responseBody = json.decode(response.body);return responseBody;} else {http.Response response =await http.post(Uri.parse(fullUrl), body: params);var responseBody = json.decode(response.body);return responseBody;}}static Uri _uriWith(String url, {Map queryParameters}) {String _url = url;String query = _urlEncodeMap(queryParameters);if (query.isNotEmpty) {_url += (_url.contains("?") ? "&" : "?") + query;}// Normalize the url.return Uri.parse(_url).normalizePath();}static String _urlEncodeMap(data) {StringBuffer urlData = StringBuffer("");bool first = true;void urlEncode(dynamic sub, String path) {if (sub is List) {for (int i = 0; i < sub.length; i++) {urlEncode(sub[i],"$path%5B${(sub[i] is Map || sub[i] is List) ? i : ''}%5D");}} else if (sub is Map) {sub.forEach((k, v) {if (path == "") {urlEncode(v, "${Uri.encodeQueryComponent(k)}");} else {urlEncode(v, "$path%5B${Uri.encodeQueryComponent(k)}%5D");}});} else {if (!first) {urlData.write("&");}first = false;urlData.write("$path=${Uri.encodeQueryComponent(sub.toString())}");}}urlEncode(data, "");return urlData.toString();} } 復(fù)制代碼markdown 渲染
在 github 上巡游一番,應(yīng)該找不到針對 flutter_web 的 markdown 的支持庫。筆者在參考
- flutter/flutter_markdown
- JakeCai/flutter_web_markdown
- alibaba/flutter-go
封裝了在 flutter_web 上可用的 markdown 組件,具體實現(xiàn)可參考 swiftclub/site
語法高亮現(xiàn)在只支持 dart 語言的。
效果
更多閱讀,請關(guān)注 SwiftOldBird 官方微信公眾號
原文:swiftoldbird.loveli.site/2019/08/22/…
轉(zhuǎn)載于:https://juejin.im/post/5d5e666ff265da03a715da36
總結(jié)
以上是生活随笔為你收集整理的flutter_web 实战之文章列表与详情的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Swagger 入门使用
- 下一篇: 被辞退员工的一天
