1. 首页

前端工程师都须知道的 Flutter 教程(下)

最爱折腾的就是前端工程师了,从 jQuery 折腾到 AngularJs,再折腾到 Vue、React。最爱跨屏的也是前端工程师,从 phonegap,折腾到 React Native,这不又折腾到了 Flutter。
图啥?低成本地为用户带来更优秀的用户体验。目前来说 Flutter 可能是其中最优秀的一种方案了。

1. Widget 布局

上说过 Flutter 布局思路来自 CSS,而 Flutter 中一切皆 Widget,因此整体布局也很简单:

  • 容器组件 Container
    • decoration 装饰属性,设置背景色,背景图,边框,圆角,阴影和渐变等
    • margin
    • padding
    • alignment
    • width
    • height
  • Padding,Center
  • Row,Column,Flex
  • Wrap, Flow 流式布局
  • stack, z 轴布局
  • ……

Flutter 中 Widget 可以分为三类,形如 React 中“展示组件”、“容器组件”,“context”。

JS中文网 – 前端进阶资源教程 www.javascriptC.com
一个致力于帮助开发者用代码改变世界为使命的平台,每天都可以在这里找到技术世界的头条内容

2. StatelessWidget

这个就是 Flutter 中的“展示组件”,自身不保存状态,外部参数变化就销毁重新创建。Flutter 建议尽量使用无状态的组件。

3. StatefulWidget

状态组件就是类似于 React 中的“容器组件”了,Flutter 中状态组件写法会稍微不一样。

复制代码


class Counter extends StatefulWidget { // This class is the configuration for the state. It holds the // values (in this case nothing) provided by the parent and used by the build // method of the State. Fields in a Widget subclass are always marked "final". @override _CounterState createState() => _CounterState(); } class _CounterState extends State<Counter> { int _counter = 0; void _increment() { setState(() { // This call to setState tells the Flutter framework that // something has changed in this State, which causes it to rerun // the build method below so that the display can reflect the // updated values. If you change _counter without calling // setState(), then the build method won't be called again, // and so nothing would appear to happen. _counter++; }); } @override Widget build(BuildContext context) { // This method is rerun every time setState is called, for instance // as done by the _increment method above. // The Flutter framework has been optimized to make rerunning // build methods fast, so that you can just rebuild anything that // needs updating rather than having to individually change // instances of widgets. JS中文网 - 前端进阶资源教程 https://javascriptc.com/ return Row( children: <Widget>[ RaisedButton( onPressed: _increment, child: Text('Increment'), ), Text('Count: $_counter'), ], ); } }

可以看到 Flutter 中直接使用了和 React 中同名的 setState 方法,不过不会有变量合并的东西,当然也有生命周期。

写给前端工程师的 Flutter 教程(下)

Flutter StatefulWidget 生命周期

可以看到一个有状态的组件需要两个 Class,这样写的原因在于,Flutter 中 Widget 都是 immmutable 的,状态组件的状态保存在 State 中,组件仍然每次重新创建,Widget 在这里只是一种对组件的描述,Flutter 会 diff 转换成 Element,然后转换成 RenderObject 才渲染。

写给前端工程师的 Flutter 教程(下)

Flutter render object

实际上 Widget 只是作为组件结构一种描述,还可以带来的好处是,你可以更方便的做一些主题性的组件, Flutter 官方提供的 Material Components widgets 和 Cupertino (iOS-style) widgets 质量就相当高,再配合 Flutter 亚秒级的 Hot Reload,开发体验可以说挺不错的。

State Management

setState() 可以很方便的管理组件内的数据,但是 Flutter 中状态同样是从上往下流转的,因此也会遇到和 React 中同样的问题,如果组件树太深,逐层状态创建就显得很麻烦了,更不要说代码的易读和易维护性了。

1. InheritedWidget

同样 Flutter 也有个 context 一样的东西,那就是 InheritedWidget,使用起来也很简单。

复制代码

class GlobalData extends InheritedWidget {
  final int count;
  GlobalData({Key key, this.count,Widget child}):super(key:key,child:child);

  @override
  bool updateShouldNotify(GlobalData oldWidget) {
    return oldWidget.count != count;
  }

  static GlobalData of(BuildContext context) => context.inheritFromWidgetOfExactType(GlobalData);
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: GlobalData(
        count: _counter,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '$_counter',
                style: Theme.of(context).textTheme.display1,
              ),
              Body(),
              Body2()
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

class Body extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    GlobalData globalData = GlobalData.of(context);
    return Text(globalData.count.toString());
  }
}

class Body2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    GlobalData globalData = GlobalData.of(context);
    return Text(globalData.count.toString());
  }

2. BlOC

BlOC 是 Flutter team 提出建议的另一种更高级的数据组织方式,也是我最中意的方式。简单来说:BlOC= InheritedWidget + RxDart(Stream)

Dart 语言中内置了 Steam,Stream ~= Observable,配合 RxDart, 然后加上 StreamBuilder 会是一种异常强大和自由的模式。

复制代码


class GlobalData extends InheritedWidget { final int count; final Stream<String> timeInterval$ = new Stream.periodic(Duration(seconds: 10)).map((time) => new DateTime.now().toString()); GlobalData({Key key, this.count,Widget child}):super(key:key,child:child); @override bool updateShouldNotify(GlobalData oldWidget) { return oldWidget.count != count; } static GlobalData of(BuildContext context) => context.inheritFromWidgetOfExactType(GlobalData); } class TimerView extends StatelessWidget { @override Widget build(BuildContext context) { GlobalData globalData = GlobalData.of(context); return StreamBuilder( stream: globalData.timeInterval$, builder: (context, snapshot) { return Text(snapshot?.data ?? ''); } ); } }

当然 Bloc 的问题在于

  • 学习成本略高,Rx 的概念要吃透,不然你会抓狂
  • 自由带来的问题是,可能代码不如 Redux 类的规整。

顺便,今年 Apple 也拥抱了响应式,Combine(Rx like) + SwiftUI 也基本等于 Bloc 了

所以,Rx 还是要赶紧学起来 :grimacing。

除去 Bloc,Flutter 中还是可以使用其他的方案,譬如:

  • Flutter Redux
  • 阿里闲鱼的 Fish Redux,据说性能很好
  • Mobx
  • ……

展开来说现在的前端开发使用强大的框架页面组装已经不是难点了。开发的难点在于如何组合富交互所需的数据,也就是上面图中的 state 部分。

更具体来说,是怎么优雅,高效,易维护地处理短暂数据 (ephemeral state)setState() 和需要共享的 App State 的问题,这是个工程性的问题,但往往也是日常开发最难的事情了,引用 Redux 作者 Dan 的一句:

“The rule of thumb is:Do whatever is less awkward.”

到这里,主要的部分已经讲完了,有这些已经可以开发出一个不错的 App 了。剩下的就当成一个 bonus 吧。

测试

Flutter debugger,测试都是出场自带,用起来也不难。

复制代码

// 测试在 /test/ 目录下面
void main() {

  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp());

    // Verify that our counter starts at 0.
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // Tap the '+' icon and trigger a frame.
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Verify that our counter has incremented.
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}

包管理,资源管理

类似与 JavaScript 的 npm,Flutter,也就是 Dart 也有自己的包仓库。不过项目包的依赖使用 yaml 文件来描述:

复制代码


name: app description: A new Flutter project. version: 1.0.0+1 environment: sdk: ">=2.1.0 <3.0.0" dependencies: flutter: sdk: flutter cupertino_icons: ^0.1.2

生命周期

移动应用总归需要应用级别的生命周期,flutter 中使用生命周期钩子,也非常的简单:

复制代码

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.inactive:
        print('AppLifecycleState.inactive');
        break;
      case AppLifecycleState.paused:
        print('AppLifecycleState.paused');
        break;
      case AppLifecycleState.resumed:
        print('AppLifecycleState.resumed');
        break;
      case AppLifecycleState.suspending:
        print('AppLifecycleState.suspending');
        break;
    }
    super.didChangeAppLifecycleState(state);
  }

  @override
  Widget build(BuildContext context) {
      return Container();
  }
}

使用原生能力

和 ReactNative 类似,Flutter 也是使用类似事件的机制来使用平台相关能力。

写给前端工程师的 Flutter 教程(下)

Flutter platform channels

Flutter Web, Flutter Desktop

这些还在开发当中,鉴于对 Dart 喜欢,以及对 Flutter 性能的乐观,这些倒是很值得期待。

写给前端工程师的 Flutter 教程(下)

Flutter Web 架构

还记得平台只是给 Flutter 提供一个画布么,Flutter Desktop 未来更是可以大有可为。最后每种方案,每种技术都有优缺点,甚至技术的架构决定了,有些缺陷可能永远都没法改进。

作者:云加社区
链接:https://www.infoq.cn/article/O6YLYs7oZeOs6pcFpPJ9

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:前端工程师都须知道的 Flutter 教程(下)

链接:https://www.javascriptc.com/2680.html

« 幻术,一行代码实现镂空效果
前端工程师都须知道的 Flutter 教程(上)»
Flutter 中文教程资源

相关推荐

QR code