鉴于Flutter的高性能渲染、跨平台、多端一致性等优点,闪点清单在移动端APP上,使用了完整的Flutter框架来开发。既然是完整APP,架构搭建彻底不受历史Native APP的影响,没有历史包袱的沉淀,设计也能更灵活和健壮。
全局BuildContext
,几乎是全部Flutter开发者的一个痛点。这个痛点有多痛呢?咱们来列举一下场景:segmentfault
在Android中,咱们能够用getApplicationContext
解决全局context问题,Flutter官方并无提供建议的方案,不过社区有一些推荐的解决方案,好比使用GlobalKey的方案:架构
@override Widget build(BuildContext context) { return MaterialApp( navigatorKey: globalNavigatorKey, // GlobalKey() ) } globalNavigatorKey.currentState.push( MaterialPageRoute(builder: (context) => SomePage()), );
首先咱们定义一个GlobalKey
,而后在初始化MaterialApp
的时候传入navigatorKey
,而后咱们在须要使用路由跳转的地方,不使用原始的方式,而使用navigatorKey来调用:框架
globalNavigatorKey.currentState.push(...)
看起来上述方案好像能够解决问题,可是目前只能解决页面路由跳转问题,而若是使用Overlays(好比Dialog)、MediaQuery等就会出现问题了,error提示context不合法:less
The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
而直接使用navigatorKey.currentState.context
获取全局context也会出现一样的error。ide
在尝试众多方案都失败后,咱们仍然在继续寻找更好的方案,最终找到了OneContext方案,仓库地址: one_context。函数
OneContext是一个很是新的库,2020年5月初才发第一个版本,目前还未发1.0版本。不过API的完成度仍是很高的。工具
使用OneContext,首先咱们须要在MaterialApp中配置OneContext:性能
MaterialApp( builder: (BuildContext context, Widget child) { return OneContext().builder(context, child, initialRoute: 'home'); }, /// builder: OneContext().builder, /// 若是不须要initialRoute,可使用这种方式 navigatorKey: OneContext().key, )
而后,须要使用context的地方,所有经过OneContext来调用:ui
OneContext().pushNamed('calendar'); OneContext().showModalBottomSheet( builder: (BuildContext context) { return Container(); }, ); OneContext().showDialog(...); OneContext().addOverlay(...);
OneContext().pushNamed('/second'); OneContext().push(MaterialPageRoute(builder: (_) => SecondPage())); OneContext().pop();
/// 展现ModalBottomSheet OneContext().showModalBottomSheet( builder: (BuildContext context) { return Container(); }, ); /// 添加移除覆盖物 OneContext().addOverlay( overlayId: myCustomAndAwesomeOverlayId, builder: (_) => MyCustomAndAwesomeOverlay() ); OneContext().removeOverlay(myCustomAndAwesomeOverlayId); /// 加载提示 OneContext().showProgressIndicator(); OneContext().showProgressIndicator( backgroundColor: Colors.blue.withOpacity(.3), circularProgressIndicatorColor: Colors.white ); OneContext().hideProgressIndicator();
print('Platform: ' + OneContext().theme.platform); print('Orientation: ' + OneContext().mediaQuery.orientation);
OneContext().oneTheme.toggleMode(); OneContext().oneTheme.changeDarkThemeData( ThemeData( primarySwatch: Colors.amber, brightness: Brightness.dark ) );
从OneContext配置中,能够看出来,OneContext最关键的一句配置是OneContext().builder
,咱们点进去看源码:this
Widget builder(BuildContext context, Widget widget, {Key key, MediaQueryData mediaQueryData, String initialRoute, Route<dynamic> Function(RouteSettings) onGenerateRoute, Route<dynamic> Function(RouteSettings) onUnknownRoute, List<NavigatorObserver> observers = const <NavigatorObserver>[]}) => ParentContextWidget( child: widget, mediaQueryData: mediaQueryData, initialRoute: initialRoute, onGenerateRoute: onGenerateRoute, onUnknownRoute: onUnknownRoute, observers: observers, ); class ParentContextWidget extends StatelessWidget { /// ... @override Widget build(BuildContext context) { return MediaQuery( data: mediaQueryData ?? MediaQuery.of(context), child: Navigator( initialRoute: initialRoute, onUnknownRoute: onUnknownRoute, observers: observers, onGenerateRoute: onGenerateRoute ?? (settings) => MaterialPageRoute( builder: (context) => OneContextWidget( child: child, )), ), ); } }
从源码中咱们能够看到:
OneContextWidget
,而后就能够在OneContextWidget拿到内层context,这个context能够用于绝大部分场景。Overlay
的经常使用方法,并绑定了内部的context对象,从而解决Overlay的context获取问题。import 'package:flutter/material.dart'; import 'package:one_context/src/controllers/one_context.dart'; class OneContextWidget extends StatefulWidget { final Widget child; OneContextWidget({Key key, this.child}) : super(key: key); _OneContextWidgetState createState() => _OneContextWidgetState(); } class _OneContextWidgetState extends State<OneContextWidget> { @override void initState() { super.initState(); OneContext().registerDialogCallback( showDialog: _showDialog, showSnackBar: _showSnackBar, showModalBottomSheet: _showModalBottomSheet, showBottomSheet: _showBottomSheet); } @override Widget build(BuildContext context) { return Scaffold( body: Builder( builder: (innerContext) { OneContext().context = innerContext; return widget.child; }, ), ); } Future<T> _showDialog<T>(...){...} ScaffoldFeatureController<SnackBar, SnackBarClosedReason> _showSnackBar(...){ ... } Future<T> _showModalBottomSheet<T>(...){ ... } PersistentBottomSheetController<T> _showBottomSheet<T>(...) { ... } }
OneContextWidget
在每次build时,会更新全局context:@override Widget build(BuildContext context) { return Scaffold( body: Builder( builder: (innerContext) { OneContext().context = innerContext; return widget.child; }, ), ); }
Navigator.pop
没法正确关闭Dialog
)OneContext().popDialog()
代替Navigator.pop
,切记切记。到目前咱们解决了Flutter全局BuildContext的问题,但这其实并不该该是最终的方案,OneContext
是一个侵入性比较高的方案,Flutter官方应该提供更好的方案来解决这个问题。
讲到这里,还并无完成基础框架的搭建,后面咱们会讲解更多的Flutter架构设计内容,好比:通知、分享、UI设计等等。
持续分享闪点清单在Flutter上的开发经验。闪点清单,一款悬浮清单软件: