Flutter Go 源碼分析(一)

前言

在經過了一段時間的Flutter學習之后,感覺自己該來點項目實戰了,在找了很多開源項目之后,最終決定學習阿里大佬們開發的Flutter Go項目,此文章以記錄自己的學習過程,感謝大佬們的無私開源,項目下載地址

目錄

Flutter Go 學習之路(二)
Flutter Go 學習之路(三)
Flutter Go 學習之路(四)
Flutter Go 學習之路(五)
Flutter Go 學習之路(六)

我們從入口main()函數開始學起,先說明一些初始化的這幾個類:

void main() async {
  final provider = new Provider();
  await provider.init(true);//創建/打開 數據庫
  sp = await SpUtil.getInstance();//用來做shared_preferences的存儲
  new SearchHistoryList(sp);//工廠方法獲取實例對象  獲取本地搜索記錄列表 單例
  db = Provider.db;
  runApp(new MyApp());
}
(1) Provider

??它是對數據庫操作相關的類,用于創建數據庫的工具,首先我們來看一下初始化方法:

//初始化數據庫
  Future init(bool isCreate) async {
    //Get a location using getDatabasesPath
    String databasesPath = await getDatabasesPath();// 獲取數據庫路徑 sqflite三方
    String path = join(databasesPath, 'flutter.db');//拼接App數據庫名稱
    print(path);
    try {//嘗試打開數據庫 如果已經創建
      db = await openDatabase(path);
    } catch (e) {
      print("Error $e");
    }
    bool tableIsRight = await this.checkTableIsRight();//檢查數據庫中表是否完整

    if (!tableIsRight) {//如果不完整就刪除重新創建
      // 關閉上面打開的db,否則無法執行open
      db.close();
      // Delete the database
      await deleteDatabase(path);
      ByteData data = await rootBundle.load(join("assets", "app.db"));//讀取assets app.db(創建表)的數據
      List<int> bytes =
          data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
      await new File(path).writeAsBytes(bytes);//將讀取的數據寫入路徑
  //打開數據庫
      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");
    }
  }

app.db表

??init方法總體實現思路就是

  • 1)先通過openDatabase(path);嘗試打開數據庫。
  • 2)再通過checkTableIsRight方法檢查數據庫是否存在或者完整:
// 檢查數據庫中, 表是否完整, 在部份android中, 會出現表丟失的情況
  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)如果沒有創建過或者表不完整就會刪除重新創建數據表await deleteDatabase(path);,在通過本地assets 下的app.db按表名寫入一個空的
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);

??最后打開數據庫。

(2) SpUtil

??這個是用來做本地化存儲的,主要代碼:

static SharedPreferences _spf;


  SpUtil._();

  Future _init() async {
    //SharedPreferences 數據本地化相關工具 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實例是一個SharedPreferences,它就相當于iOS的NSUserDefaults或者安卓的SharedPreferences.剩下的其他一些方法都是些寫入或者讀取的方法,這里就不過多闡述了。

(3) SearchHistoryList

?? SearchHistoryList是利用SpUtil用來存取搜索記錄的工具我們主要來看一下它的構造方法:

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;
  }
  //工廠模式 實現
  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 = [];

  // 存放的最大數量
  int _count = 10;

  SearchHistoryList.fromJSON(String jsonData) {
    _searchHistoryList = [];//存儲的是SearchHistory  model
    if (jsonData == null) {
      return;
    }
    List jsonList = json.decode(jsonData);
    jsonList.forEach((value) {
      _searchHistoryList.add(SearchHistory(
          name: value['name'], targetRouter: value['targetRouter']));
    });
  }

執行順序是:factory SearchHistoryList([SpUtil sp])-->static SearchHistoryList _getInstance(SpUtil sp)-->SearchHistoryList.fromJSON(String jsonData)并且在初始化的時候搜索記錄列表就已經取出存到了_searchHistoryList里面,其他的一些存取方法也不過多闡述。

(4) MyApp() 主程序框架
class MyApp extends StatelessWidget {
  MyApp()  {
    final router = new Router();

    Routes.configureRoutes(router);

    Application.router = router;
  }
  showWelcomePage() {
    // 暫時關掉歡迎介紹
    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(
          //設置Material的默認字體樣式
          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],
    );
  }
}

這里構造方法里面進行了Router的初始化和注冊,用到了fluro庫,具體用法大家自行去了解,不再做闡述。
在flutter應用中,一般一個應用都對應一個MaterialApp這個組件是flutter應用程序的骨架:

    this.navigatorKey, // 導航的key
    this.home, // 主頁
    this.routes = const <String, WidgetBuilder>{},// 路由
    this.initialRoute,//初始路由
    this.onGenerateRoute,//生成路由
    this.onUnknownRoute,//位置路由
    this.navigatorObservers = const <NavigatorObserver>[],//導航的觀察者
    this.builder,//widget的構建
    this.title = '',//設備用于識別用戶的應用程序的單行描述。在Android上,標題顯示在任務管理器的應用程序快照上方,當用戶按下“最近的應用程序”按鈕時會顯示這些快照。 在iOS上,無法使用此值。 來自應用程序的`Info.plist`的`CFBundleDisplayName`在任何時候都會被引用,否則就會引用`CFBundleName`。要提供初始化的標題,可以用 onGenerateTitle。
    this.onGenerateTitle,//每次在WidgetsApp構建時都會重新生成
    this.color,//背景顏色
    this.theme,//主題,用ThemeData
    this.locale,//app語言支持
    this.localizationsDelegates,//多語言代理
    this.localeResolutionCallback,//
    this.supportedLocales = const <Locale>[Locale('en', 'US')],//支持的多語言
    this.debugShowMaterialGrid = false,//顯示網格
    this.showPerformanceOverlay = false,//打開性能監控,覆蓋在屏幕最上面
    this.checkerboardRasterCacheImages = false,
    this.checkerboardOffscreenLayers = false,
    this.showSemanticsDebugger = false,//打開一個覆蓋圖,顯示框架報告的可訪問性信息 顯示邊框
    this.debugShowCheckedModeBanner = true,//右上角顯示一個debug的圖標

這里主要說一下onGenerateRoute: Application.router.generator,這句代碼,這里是配合fluro庫使用的,上面代碼也可以注釋掉:

home: new Scaffold(
        body: showWelcomePage()
      ),

然后再注冊的時候注冊一下"/"就好:

router.define("/", handler: homeHandler);
// app的首頁
var homeHandler = new Handler(
  handlerFunc: (BuildContext context, Map<String, List<String>> params) {
    return new AppPage();
  },
);

其余屬性自行去了解。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容