Flutter State Management状态管理全面分析(一)

简介: Flutter State Management状态管理全面分析

前言


2019 Google I/O 大会,google就推出Provider,成为官方推荐的状态管理方式之一,Flutter 状态管理一直是个很热门的话题,而且状态管理的库也是超级多,这确实是我们每一个做Flutter开发难以避免的一道坎,既然这么重要,我们如何去理解它,如何使用它,如何做到更好呢?接下来让我告诉你答案

啰嗦几句


该文章已经经历了一周的迭代,预计还要一周左右,要做一个全面的分析,当然要每个细节都要关注到,如果您觉得好,请不要吝啬您的大拇指,顺便点个赞哦,么么哒,如果有不对的地方提出来,一个地方一个红包奖励哦,爱你们。

主要内容


image.png

一张图告诉你,我要讲的主要内容。下面将围绕这八个方面来讲。七个理论,一个实践。

  • 状态管理是什么
  • 为什么需要状态管理
  • 状态管理基本分类
  • 状态管理的底层逻辑
  • 状态管理的使用原则
  • 使用成熟状态管理库的弊端
  • 选择状态管理库的原则
  • Provider 深入分析(学以致用)

状态管理是什么


我们知道最基本的程序是什么:

  • 程序=算法+数据结构数据是程序的中心。数据结构和算法两个概念间的逻辑关系贯穿了整个程序世界,首先二者表现为不可分割的关系。其实Flutter不就是一个程序吗,那我们面临的最底层的问题还是算法和数据结构,所以我们推导出
  • Flutter=算法+数据结构那状态管理是什么?我也用公式来表达一下,如下:
  • Flutter状态管理=算法+数据结构+UI绑定
    瞬间秒懂有没有?来看一个代码例子:
class ThemeBloc {
  final _themeStreamController = StreamController<AppTheme>();
  get changeTheTheme => _themeStreamController.sink.add;
  get darkThemeIsEnabled => _themeStreamController.stream;
  dispose() {
    _themeStreamController.close();
  }
}
final bloc = ThemeBloc();
class AppTheme {
  ThemeData themeData;
  AppTheme(this.themeData);
}
/// 绑定到UI
StreamBuilder<AppTheme>(
        initialData: AppTheme.LIGHT_THEME,
        stream: bloc.darkThemeIsEnabled,
        builder: (context, AsyncSnapshot<AppTheme> snapshot) {
          return MaterialApp(
            title: 'Jetpack',
            theme: snapshot.data.themeData,
            home: PageHome(),
            routes: <String, WidgetBuilder>{
              "/pageChatGroup": (context) => PageChatGroup(),
              "/LaoMeng": (context) => LaoMeng(),
            },
          );
        })
  • AppTheme 是数据结构
  • changeTheTheme 是算法
  • StreamBuilder 是绑定UI

这样一整套代码的逻辑就是我们所说的Flutter状态管理,这样解释大家理解了吗?再细说,算法就是我们如何管理,数据结构就是数据状态,状态管理的本质还是如何通过合理的算法管理数据,如何取,如何接收等,最终展示在UI上,通过UI的变更来体现状态的管理逻辑。

为什么需要


这里就需要明白一个事情,Flutter的很多优秀的设计都来源于React,对于react来说,同级组件之间的通信尤为麻烦,或者是非常麻烦了,所以我们把所有需要多个组件使用的state拿出来,整合到顶部容器,进行分发。状态管理可以实现组件通信、跨组件数据储存。推荐阅读对 React 状态管理的理解及方案对比,那么对于Flutter来说呢?你知道Android、Ios等原生于Flutter最本质的区别吗?来看一段代码:

//android
TextView tv = TextView()
tv.setText("text")
///flutter
setState{
    text = "text"
}

从上面代码我们看出,Android的状态变更是通过具体的组件直接赋值,如果页面全部变更,你是不是需要每一个都设置一遍呢?,而Flutter的变更就简单粗暴,setState搞定,它背后的逻辑是重新build整个页面,发现有变更,再将新的数据赋值,其实Android、Ios与flutter的本质的区别就是数据与视图完全分离,当然Android也出现了UI绑定框架,似乎跟React、Flutter越来越像,所以这也在另一方面凸显出了,Flutter设计的先进性,没有什么创新,但更符合未来感,回过头来,仔细想一想,这样设计有什么弊端?

对了你猜对了:页面如何刷新才是Flutter的关键,做Android的同学肯定也面临着一个问题,页面的重绘导致的丢帧问题,为了更好,我们很多时候都选择了局部刷新来优化对吧,Android、Ios已经很明确的告诉UI要刷新什么更新什么,而对于Flutter来说,这一点很不清晰,虽然Flutter也做了类似虚拟Dom优化重绘逻辑,但这些远远不够的,如何合理的更新UI才是最主要的,这个时候一大堆的状态管理就出来了,当然状态管理也不是仅仅为了解决更新问题。

我再抛出一个问题,如果我有一个widget A,我想在另外一个widget B中改变widget A的一个状态,或者从网络、数据库取到数据,然后刷新它,怎么做?我们来模拟一下,来看代码

糟糕的状态管理代码

class WidgetTest extends StatefulWidget {
  @override
  _WidgetTestState createState() => _WidgetTestState();
}
class _WidgetTestState extends State<WidgetTest> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          WidgetA(),
          WidgetB()
        ],
      ),
    );
  }
}
_WidgetAState _widgetAState;
class WidgetA extends StatefulWidget {
  @override
  _WidgetAState createState() {
    _widgetAState = _WidgetAState();
    return _widgetAState;
  }
}
class _WidgetAState extends State<WidgetA> {
  var title = "";
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(title),
    );
  }
}
class WidgetB extends StatefulWidget {
  @override
  _WidgetBState createState() => _WidgetBState();
}
class _WidgetBState extends State<WidgetB> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: RaisedButton(
        onPressed: () {
          _widgetAState.setState(() {
            _widgetAState.title = "WidgetB";
          });
        },
      ),
    );
  }
}

WidgetTest页面有两个widget,分别是WidgetA、WidgetB,WidgetB通过RaisedButton的onPressed来改变WidgetA的Text,怎么做到的呢,直接用WidgetA的_WidgetAState对象提供的setState函数来变更,没什么问题对吧,而且功能实现了,但你仔细思考一下,这有什么问题呢?

  • WidgetAState 被全局化,而且它所有状态被暴漏出去,如果_WidgetAState有十个状态,只有一个想让别人变更,可惜已经晚了, 你加''也不行,组件的隐私全没了
  • 耦合变高,WidgetB有_WidgetAState的强关联,我们编码追求的解偶,在这里完全被忽视了
  • 性能变差,为什么这么说?因为每次_widgetAState.setState都会导致整个页面甚至子Widget的重新build,如果_widgetAState里面有成千上百的状态,性能肯定差到极点
  • 不可测,程序变得难以测试

如何变好呢这就需要选择一种合适的状态管理方式。

状态管理的目标其实我们做状态管理,不仅仅是因为它的特点,而为了更好架构,不是吗?

  • 代码要层次分明,易维护,易阅读
  • 可扩展,易维护,可以动态替换UI而不影响算法逻辑
  • 安全可靠,保持数据的稳定伸缩
  • 性能佳,局部优化

这些不紧紧是状态管理的目的,也是我们做一款优秀应用的基础架构哦。

基本分类


  • 局部管理  官方也称 Ephemeral state,意思是短暂的状态,这种状态根本不需要做全局处理 举个例子,如下方的_index,这就是一个局部或者短暂状态,只需要StatefulWidget处理即可完成
class MyHomepage extends StatefulWidget {
  @override
  _MyHomepageState createState() => _MyHomepageState();
}
class _MyHomepageState extends State<MyHomepage> {
  int _index = 0;
  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
      currentIndex: _index,
      onTap: (newIndex) {
        setState(() {
          _index = newIndex;
        });
      },
      // ... items ...
    );
  }
}
  • 全局管理 官方称 App state,即应用状态,非短暂状态,您要在应用程序的许多部分之间共享,以及希望在用户会话之间保持的状态,就是我们所说的应用程序状态(有时也称为共享状态) 例如:
  • 用户偏好
  • 登录信息
  • 购物车
  • 新闻阅读状态

状态分类官方定义

没有明确的通用规则来区分特定变量是短暂状态还是应用程序状态。有时,您必须将一个重构为另一个。例如,您将从一个明显的短暂状态开始,但是随着您的应用程序功能的增长,可能需要将其移至应用程序状态。 出于这个原因,请使用下图进行分类:

image.png

总之,任何Flutter应用程序中都有两种概念性的状态类型。临时状态可以使用State和setState()来实现,并且通常是单个窗口小部件的本地状态。剩下的就是您的应用状态。两种类型在任何Flutter应用程序中都有自己的位置,两者之间的划分取决于您自己的喜好和应用程序的复杂性

没有最好的管理方式,只有最合适的管理方式

底层逻辑


底层逻辑我想告诉你的是,Flutter中目前有哪些可以做到状态管理,有什么缺点,适合做什么不适合做什么,只有你完全明白底层逻辑,才不会畏惧复杂的逻辑,即使是复杂的逻辑,你也能选择合理的方式去管理状态。

  • State
    StatefulWidget、StreamBuilder状态管理方式
  • InheritedWidget 专门负责Widget树中数据共享的功能型Widget,如Provider、scoped_model就是基于它开发
  • Notification 与InheritedWidget正好相反,InheritedWidget是从上往下传递数据,Notification是从下往上,但两者都在自己的Widget树中传递,无法跨越树传递。
  • Stream 数据流 如Bloc、flutter_redux、fish_redux等也都基于它来做实现 为什么列这些东西?因为现在大部分流行的状态管理都离不开它们。理解它们比理解那些吹自己牛逼的框架要好的多。请关注底层逻辑,这样你才能游刃有余。下面我们一个个分析一下:

State


State 是我们常用而且使用最频繁的一个状态管理类,它必须结合StatefulWidget一起使用,StreamBuilder继承自StatefulWidget,同样是通过setState来管理状态

举个例子来看下:

class TapboxA extends StatefulWidget {
  TapboxA({Key key}) : super(key: key);
  @override
  _TapboxAState createState() => _TapboxAState();
}
class _TapboxAState extends State<TapboxA> {
  bool _active = false;
  void _handleTap() {
    setState(() {
      _active = !_active;
    });
  }
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            _active ? 'Active' : 'Inactive',
            style: TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: _active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

引用官方的例子,这里_active状态就是通过State提供的setState函数来实现的 为什么会让State去管理状态,而不是Widget本身呢?Flutter设计时让Widget本身是不变的,类似固定的配置信息,那么就需要一个角色来控制它,State就出现了,但State的任何更改都会强制整个Widget重新构建,当然你也可以覆盖必要方法自己控制逻辑。

再看个例子:

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;
  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}
//------------------------- TapboxB ----------------------------------
class TapboxB extends StatelessWidget {
  TapboxB({Key key, this.active: false, @required this.onChanged})
      : super(key: key);
  final bool active;
  final ValueChanged<bool> onChanged;
  void _handleTap() {
    onChanged(!active);
  }
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            style: TextStyle(fontSize: 32.0, color: Colors.white),
          ),
        ),
        width: 200.0,
        height: 200.0,
        decoration: BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
      ),
    );
  }
}

从这里你看出什么?对了,父组件可以通过setState来刷新子Widget的状态变化,所以得出如下观点注意setState是整个Widget重新构建(而且子Widget也会跟着销毁重建),这个点也是为什么不推荐你大量使用StatefulWidget的原因。如果页面足够复杂,就会导致严重的性能损耗。如何优化呢?建议使用StreamBuilder,它原理上也是State,但它做到了子Widget的局部刷新,不会导致整个页面的重建,是不是就好很多了呢?

State缺点

从上面的代码我们分析一下它的缺点

  • 无法做到跨组件共享数据(这个跨是无关联的,如果是直接的父子关系,我们不认为是跨组件) setState是State的函数,一般我们会将State的子类设置为私有,所以无法做到让别的组件调用State的setState函数来刷新
  • setState会成为维护的难点,因为啥哪哪都是。 随着页面状态的增多,你可能在调用setState的地方会越来越多,不能统一管理
  • 处理数据逻辑和视图混合在一起,违反代码设计原则 比如数据库的数据取出来setState到Ui上,这样编写代码,导致状态和UI耦合在一起,不利于测试,不利于复用。

State小结

当然反过来讲,不是因为它有缺点我们就不使用了,我们追求的简单高效,简单实现,高效运行,当复杂到需要更好的管理的时候再重构。一个基本原则就是,状态是否需要跨组件使用,如果需要那就用别的办法管理状态而不是State管理。

InheritedWidget


InheritedWidget是一个无私的Widget,它可以把自己的状态数据,无私的交给所有的子Widget,所有的子Widget可以无条件的继承它的状态。就这么一个东西。有了State我们为什么还需要它呢?我们已经知道,State是可以更新直接子Widget的状态,但如果是子Widget的子Widget呢,所以说InheritedWidget的存在,一是为了更简单的获取状态,二是大家都共享这个状态,举个例子

class InheritedWidgetDemo extends InheritedWidget {
  final int accountId;
  InheritedWidgetDemo(this.accountId, {Key key, Widget child})
      : super(key: key, child: child);
  @override
  bool updateShouldNotify(InheritedWidgetDemo old) =>
      accountId != old.accountId;
  static InheritedWidgetDemo of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<InheritedWidgetDemo>();
  }
}
class MyPage extends StatelessWidget {
  final int accountId;
  MyPage(this.accountId);
  Widget build(BuildContext context) {
    return new InheritedWidgetDemo(
      accountId,
      child: const MyWidget(),
    );
  }
}
class MyWidget extends StatelessWidget {
  const MyWidget();
  Widget build(BuildContext context) {
    return MyOtherWidget();
  }
}
class MyOtherWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final myInheritedWidget = InheritedWidgetDemo.of(context);
    print(myInheritedWidget.accountId);
  }
}
  • InheritedWidgetDemo共享状态accountId给了MyOtherWidget,而MyOtherWidget是MyWidget的子Widget,这就是InheritedWidget的功效,它可以做到跨组件共享状态。
  • const MyWidget() 表示该Widget是常量,不会因为页面的刷新导致重新build,这就是优化的细节,这里想一下,如果你用State实现,不是就需要它setState才能实现MyOtherWidget的重新build,这样做的坏处就是导致整个UI的刷新。
  • updateShouldNotify 它也是一个优化点,在你横屏变竖屏的同时,导致整个UI重新build,可由于updateShouldNotify的判断,系统将不会重新build MyOtherWidget,也是一种布局优化。
  • 子树中的组件通过InheritedWidgetDemo.of(context)访问共享状态。

有的人想了,InheritedWidget这么好用,那我把整个App的状态都存进来怎么样?类似这样

class AppContext {
  int teamId;
  String teamName;
  int studentId;
  String studentName;
  int classId;
  ...
}

其实这样不好,我们不光是要做技术上的组件化,更要关注的是业务,对业务的充分理解并实现模块化分工,在使用InheritedWidget时候特别是要注意这一点,更推荐你使用该方案:

class TeamContext {
  int teamId;
  String teamName;
}
class StudentContext {
  int studentId;
  String studentName;
}
class ClassContext {
  int classId;
  ...
}

注意它的数据是只读的,虽然很无私,但子widget不能修改,那么如何修改呢? 举个例子:

class Item {
   String reference;
   Item(this.reference);
}
class _MyInherited extends InheritedWidget {
  _MyInherited({
    Key key,
    @required Widget child,
    @required this.data,
  }) : super(key: key, child: child);
  final MyInheritedWidgetState data;
  @override
  bool updateShouldNotify(_MyInherited oldWidget) {
    return true;
  }
}
class MyInheritedWidget extends StatefulWidget {
  MyInheritedWidget({
    Key key,
    this.child,
  }): super(key: key);
  final Widget child;
  @override
  MyInheritedWidgetState createState() => new MyInheritedWidgetState();
  static MyInheritedWidgetState of(BuildContext context){
    return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
  }
}
class MyInheritedWidgetState extends State<MyInheritedWidget>{
  /// List of Items
  List<Item> _items = <Item>[];
  /// Getter (number of items)
  int get itemsCount => _items.length;
  /// Helper method to add an Item
  void addItem(String reference){
    setState((){
      _items.add(new Item(reference));
    });
  }
  @override
  Widget build(BuildContext context){
    return new _MyInherited(
      data: this,
      child: widget.child,
    );
  }
}
class MyTree extends StatefulWidget {
  @override
  _MyTreeState createState() => new _MyTreeState();
}
class _MyTreeState extends State<MyTree> {
  @override
  Widget build(BuildContext context) {
    return new MyInheritedWidget(
      child: new Scaffold(
        appBar: new AppBar(
          title: new Text('Title'),
        ),
        body: new Column(
          children: <Widget>[
            new WidgetA(),
            new Container(
              child: new Row(
                children: <Widget>[
                  new Icon(Icons.shopping_cart),
                  new WidgetB(),
                  new WidgetC(),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return new Container(
      child: new RaisedButton(
        child: new Text('Add Item'),
        onPressed: () {
          state.addItem('new item');
        },
      ),
    );
  }
}
class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return new Text('${state.itemsCount}');
  }
}
class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Text('I am Widget C');
  }
}

该例子引用自widget-state-context-inheritedwidget/欢迎阅读学习哦

  • _MyInherited是InheritedWidget,每次我们通过单击“ Widget A”的按钮添加元素时都会重新创建
  • MyInheritedWidget是一个状态为包含元素列表的窗口小部件。可通过(BuildContext上下文)的静态MyInheritedWidgetState访问此状态
  • MyInheritedWidgetState公开一个getter(itemsCount)和一个方法(addItem),以便子控件树的一部分的控件可以使用它们
  • 每次我们向State添加元素时,都会重新构建MyInheritedWidgetState
  • MyTree类仅构建一个小部件树,将MyInheritedWidget作为该树的父级
  • WidgetA是一个简单的RaisedButton,按下该按钮时,会调用最近的MyInheritedWidget的addItem方法。
  • WidgetB是一个简单的Text,它显示在最接近的 MyInheritedWidget级别上显示的元素数量


目录
相关文章
|
3月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
291 4
|
3月前
|
缓存 监控 前端开发
优化 Flutter 应用启动速度的策略,涵盖理解启动过程、资源加载优化、减少初始化工作、界面布局优化、异步初始化、预加载关键数据、性能监控与分析等方面
本文探讨了优化 Flutter 应用启动速度的策略,涵盖理解启动过程、资源加载优化、减少初始化工作、界面布局优化、异步初始化、预加载关键数据、性能监控与分析等方面,并通过案例分析展示了具体措施和效果,强调了持续优化的重要性及未来优化方向。
103 10
|
3月前
|
存储 JavaScript 前端开发
在Flutter开发中,状态管理至关重要。随着应用复杂度的提升,有效管理状态成为挑战
在Flutter开发中,状态管理至关重要。随着应用复杂度的提升,有效管理状态成为挑战。本文介绍了几种常用的状态管理框架,如Provider和Redux,分析了它们的基本原理、优缺点及适用场景,并提供了选择框架的建议和使用实例,旨在帮助开发者提高开发效率和应用性能。
52 4
|
3月前
|
存储 Shell 开发工具
Flutter&鸿蒙next 中使用 MobX 进行状态管理
本文介绍了如何在 Flutter 中使用 MobX 进行状态管理。MobX 是一个基于观察者模式的响应式编程库,通过 `@observable` 和 `@action` 注解管理状态,并使用 `Observer` 小部件自动更新 UI。文章详细讲解了 MobX 的核心概念、如何集成到 Flutter 项目中以及具体的代码示例。适合希望在 Flutter 应用中实现高效状态管理的开发者阅读。
132 9
|
3月前
|
存储 开发者
Flutter&鸿蒙next 使用 BLoC 模式进行状态管理详解
本文详细介绍了如何在 Flutter 中使用 BLoC 模式进行状态管理。BLoC 模式通过将业务逻辑与 UI 层分离,利用 Streams 和 Sinks 实现状态管理和 UI 更新,提高代码的可维护性和可测试性。文章涵盖了 BLoC 的基本概念、实现步骤及代码示例,包括定义 Event 和 State 类、创建 Bloc 类、提供 Bloc 实例以及通过 BlocBuilder 更新 UI。通过一个简单的计数器应用示例,展示了 BLoC 模式的具体应用和代码实现。
131 1
|
3月前
|
开发工具 开发者
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
本文深入探讨了 Flutter 中 Provider 的高级用法,涵盖多 Provider 组合、Selector 优化性能、ChangeNotifierProxyProvider 管理依赖关系以及自定义 Provider。通过这些技巧,开发者可以构建高效、可维护的响应式应用。
114 2
|
4月前
|
开发工具 开发者
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
Flutter&鸿蒙next 状态管理高级使用:深入探讨 Provider
|
3月前
|
缓存 JavaScript API
Flutter&鸿蒙next 状态管理框架对比分析
在 Flutter 开发中,状态管理至关重要,直接影响应用的性能和可维护性。本文对比分析了常见的状态管理框架,包括 setState()、InheritedWidget、Provider、Riverpod、Bloc 和 GetX,详细介绍了它们的优缺点及适用场景,并提供了 Provider 的示例代码。选择合适的状态管理框架需考虑应用复杂度、团队熟悉程度和性能要求。
156 0
|
4月前
【Flutter】状态管理:Provider状态管理
【Flutter】状态管理:Provider状态管理
28 0
|
JavaScript 容器
Flutter如何状态管理
Flutter如何状态管理
351 0

热门文章

最新文章

  • 1
    【05】flutter完成注册页面完善样式bug-增加自定义可复用组件widgets-严格规划文件和目录结构-规范入口文件-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 2
    【01】vs-code如何配置flutter环境-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈-供大大的学习提升
  • 3
    flutter开发-figma交互设计图可以转换为flutter源代码-如何将设计图转换为flutter源代码-优雅草央千澈
  • 4
    【03】完整flutter的APP打包流程-以apk设置图标-包名-签名-APP名-打包流程为例—-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈 章节内容【03】
  • 5
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 6
    【07】flutter完成主页-完成底部菜单栏并且做自定义组件-完整短视频仿抖音上下滑动页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 7
    【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 8
    【06】flutter完成注册页面-密码登录-手机短信验证-找回密码相关页面-并且实现静态跳转打包demo做演示-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
  • 9
    flutter开发中Use ‘const’ with the constructor to improve performance. Try adding the ‘const’ keyword to the constructor invocation.报错如何解决-优雅草卓伊凡
  • 10
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex