美文网首页FlutterFlutter
Flutter Go 源码分析(一)

Flutter Go 源码分析(一)

作者: Hedgehog___ | 来源:发表于2019-06-05 11:12 被阅读114次

前言

在经过了一段时间的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();
  },
);

其余属性自行去了解。

相关文章

网友评论

    本文标题:Flutter Go 源码分析(一)

    本文链接:https://www.haomeiwen.com/subject/qhjctctx.html