前言
在經(jīng)過了一段時(shí)間的Flutter學(xué)習(xí)之后迹缀,感覺自己該來點(diǎn)項(xiàng)目實(shí)戰(zhàn)了曲横,在找了很多開源項(xiàng)目之后,最終決定學(xué)習(xí)阿里大佬們開發(fā)的Flutter Go項(xiàng)目,此文章以記錄自己的學(xué)習(xí)過程拧晕,感謝大佬們的無私開源,項(xiàng)目下載地址梅垄。
目錄
Flutter Go 學(xué)習(xí)之路(二)
Flutter Go 學(xué)習(xí)之路(三)
Flutter Go 學(xué)習(xí)之路(四)
Flutter Go 學(xué)習(xí)之路(五)
Flutter Go 學(xué)習(xí)之路(六)
我們從入口main()
函數(shù)開始學(xué)起厂捞,先說明一些初始化的這幾個(gè)類:
void main() async {
final provider = new Provider();
await provider.init(true);//創(chuàng)建/打開 數(shù)據(jù)庫
sp = await SpUtil.getInstance();//用來做shared_preferences的存儲(chǔ)
new SearchHistoryList(sp);//工廠方法獲取實(shí)例對(duì)象 獲取本地搜索記錄列表 單例
db = Provider.db;
runApp(new MyApp());
}
(1) Provider
??它是對(duì)數(shù)據(jù)庫操作相關(guān)的類,用于創(chuàng)建數(shù)據(jù)庫的工具队丝,首先我們來看一下初始化方法:
//初始化數(shù)據(jù)庫
Future init(bool isCreate) async {
//Get a location using getDatabasesPath
String databasesPath = await getDatabasesPath();// 獲取數(shù)據(jù)庫路徑 sqflite三方
String path = join(databasesPath, 'flutter.db');//拼接App數(shù)據(jù)庫名稱
print(path);
try {//嘗試打開數(shù)據(jù)庫 如果已經(jīng)創(chuàng)建
db = await openDatabase(path);
} catch (e) {
print("Error $e");
}
bool tableIsRight = await this.checkTableIsRight();//檢查數(shù)據(jù)庫中表是否完整
if (!tableIsRight) {//如果不完整就刪除重新創(chuàng)建
// 關(guān)閉上面打開的db靡馁,否則無法執(zhí)行open
db.close();
// Delete the database
await deleteDatabase(path);
ByteData data = await rootBundle.load(join("assets", "app.db"));//讀取assets app.db(創(chuàng)建表)的數(shù)據(jù)
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes);//將讀取的數(shù)據(jù)寫入路徑
//打開數(shù)據(jù)庫
db = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
print('db created version is $version');
}, onOpen: (Database db) async {
print('new db opened');
});
} else {
print("Opening existing database");
}
}
??
init
方法總體實(shí)現(xiàn)思路就是
- 1)先通過
openDatabase(path);
嘗試打開數(shù)據(jù)庫。 - 2)再通過
checkTableIsRight
方法檢查數(shù)據(jù)庫是否存在或者完整:
// 檢查數(shù)據(jù)庫中, 表是否完整, 在部份android中, 會(huì)出現(xiàn)表丟失的情況
Future checkTableIsRight() async {
List<String> expectTables = ['cat', 'widget', 'collection'];
List<String> tables = await getTables();
for(int i = 0; i < expectTables.length; i++) {
if (!tables.contains(expectTables[i])) {
return false;
}
}
return true;
}
- 3)如果沒有創(chuàng)建過或者表不完整就會(huì)刪除重新創(chuàng)建數(shù)據(jù)表
await deleteDatabase(path);
,在通過本地assets 下的app.db按表名寫入一個(gè)空的
ByteData data = await rootBundle.load(join("assets", "app.db"));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes);
??最后打開數(shù)據(jù)庫机久。
(2) SpUtil
??這個(gè)是用來做本地化存儲(chǔ)的臭墨,主要代碼:
static SharedPreferences _spf;
SpUtil._();
Future _init() async {
//SharedPreferences 數(shù)據(jù)本地化相關(guān)工具 NSUserDefaults (on iOS) and SharedPreferences (on Android)
_spf = await SharedPreferences.getInstance();
}
static Future<SpUtil> getInstance() async {
if (_instance == null) {
_instance = new SpUtil._();
await _instance._init();
}
return _instance;
}
這里我們看_spf
實(shí)例是一個(gè)SharedPreferences
,它就相當(dāng)于iOS的NSUserDefaults
或者安卓的SharedPreferences
.剩下的其他一些方法都是些寫入或者讀取的方法,這里就不過多闡述了膘盖。
(3) SearchHistoryList
?? SearchHistoryList
是利用SpUtil
用來存取搜索記錄的工具我們主要來看一下它的構(gòu)造方法:
static SpUtil _sp;
static SearchHistoryList _instance;
static List<SearchHistory> _searchHistoryList = [];
static SearchHistoryList _getInstance(SpUtil sp) {
if (_instance == null) {
_sp = sp;
String json = sp.get(SharedPreferencesKeys.searchHistory);
_instance = new SearchHistoryList.fromJSON(json);//初始化方法
}
return _instance;
}
//工廠模式 實(shí)現(xiàn)
factory SearchHistoryList([SpUtil sp]) {
if (sp == null && _instance == null) {
print(new ArgumentError(
['SearchHistoryList need instantiatied SpUtil at first timte ']));
}
return _getInstance(sp);
}
// List<SearchHistory> _searchHistoryList = [];
// 存放的最大數(shù)量
int _count = 10;
SearchHistoryList.fromJSON(String jsonData) {
_searchHistoryList = [];//存儲(chǔ)的是SearchHistory model
if (jsonData == null) {
return;
}
List jsonList = json.decode(jsonData);
jsonList.forEach((value) {
_searchHistoryList.add(SearchHistory(
name: value['name'], targetRouter: value['targetRouter']));
});
}
執(zhí)行順序是:factory SearchHistoryList([SpUtil sp])
-->static SearchHistoryList _getInstance(SpUtil sp)
-->SearchHistoryList.fromJSON(String jsonData)
并且在初始化的時(shí)候搜索記錄列表就已經(jīng)取出存到了_searchHistoryList
里面胧弛,其他的一些存取方法也不過多闡述。
(4) MyApp()
主程序框架
class MyApp extends StatelessWidget {
MyApp() {
final router = new Router();
Routes.configureRoutes(router);
Application.router = router;
}
showWelcomePage() {
// 暫時(shí)關(guān)掉歡迎介紹
return AppPage();
// bool showWelcome = sp.getBool(SharedPreferencesKeys.showWelcome);
// if (showWelcome == null || showWelcome == true) {
// return WelcomePage();
// } else {
// return AppPage();
// }
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'title',
theme: new ThemeData(
primaryColor: Color(ThemeColor),
backgroundColor: Color(0xFFEFEFEF),
accentColor: Color(0xFF888888),
textTheme: TextTheme(
//設(shè)置Material的默認(rèn)字體樣式
body1: TextStyle(color: Color(0xFF888888), fontSize: 16.0),
),
iconTheme: IconThemeData(
color: Color(ThemeColor),
size: 35.0,
),
),
home: new Scaffold(
body: showWelcomePage()
),
onGenerateRoute: Application.router.generator,
navigatorObservers: <NavigatorObserver>[Analytics.observer],
);
}
}
這里構(gòu)造方法里面進(jìn)行了Router的初始化和注冊(cè)侠畔,用到了fluro庫,具體用法大家自行去了解结缚,不再做闡述。
在flutter應(yīng)用中软棺,一般一個(gè)應(yīng)用都對(duì)應(yīng)一個(gè)MaterialApp
這個(gè)組件是flutter應(yīng)用程序的骨架:
this.navigatorKey, // 導(dǎo)航的key
this.home, // 主頁
this.routes = const <String, WidgetBuilder>{},// 路由
this.initialRoute,//初始路由
this.onGenerateRoute,//生成路由
this.onUnknownRoute,//位置路由
this.navigatorObservers = const <NavigatorObserver>[],//導(dǎo)航的觀察者
this.builder,//widget的構(gòu)建
this.title = '',//設(shè)備用于識(shí)別用戶的應(yīng)用程序的單行描述红竭。在Android上,標(biāo)題顯示在任務(wù)管理器的應(yīng)用程序快照上方喘落,當(dāng)用戶按下“最近的應(yīng)用程序”按鈕時(shí)會(huì)顯示這些快照茵宪。 在iOS上,無法使用此值瘦棋。 來自應(yīng)用程序的`Info.plist`的`CFBundleDisplayName`在任何時(shí)候都會(huì)被引用稀火,否則就會(huì)引用`CFBundleName`。要提供初始化的標(biāo)題兽狭,可以用 onGenerateTitle憾股。
this.onGenerateTitle,//每次在WidgetsApp構(gòu)建時(shí)都會(huì)重新生成
this.color,//背景顏色
this.theme,//主題鹿蜀,用ThemeData
this.locale,//app語言支持
this.localizationsDelegates,//多語言代理
this.localeResolutionCallback,//
this.supportedLocales = const <Locale>[Locale('en', 'US')],//支持的多語言
this.debugShowMaterialGrid = false,//顯示網(wǎng)格
this.showPerformanceOverlay = false,//打開性能監(jiān)控箕慧,覆蓋在屏幕最上面
this.checkerboardRasterCacheImages = false,
this.checkerboardOffscreenLayers = false,
this.showSemanticsDebugger = false,//打開一個(gè)覆蓋圖服球,顯示框架報(bào)告的可訪問性信息 顯示邊框
this.debugShowCheckedModeBanner = true,//右上角顯示一個(gè)debug的圖標(biāo)
這里主要說一下onGenerateRoute: Application.router.generator,
這句代碼,這里是配合fluro
庫使用的颠焦,上面代碼也可以注釋掉:
home: new Scaffold(
body: showWelcomePage()
),
然后再注冊(cè)的時(shí)候注冊(cè)一下"/"
就好:
router.define("/", handler: homeHandler);
// app的首頁
var homeHandler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return new AppPage();
},
);
其余屬性自行去了解斩熊。