深入理解Flutter:StatefulWidget生命周期全解析(一)

前言

在Flutter开发中,StatefulWidget是构建动态、交互式界面的核心组件。与StatelessWidget不同,StatefulWidget具有可变的状态(State),能够在应用运行期间更新UI。理解StatefulWidget的生命周期对于编写高效、稳定的Flutter应用至关重要。本文将深入解析StatefulWidget生命周期的各个阶段,帮助开发者掌握状态管理的精髓。

1. StatefulWidget生命周期函数概览

StatefulWidget的生命周期可以分为创建、更新和销毁三个阶段,每个阶段都有特定的方法被调用。我们可以通过代码去验证这个结论。

2. 创建阶段(Creation Phase)

2.1 constructor()

构造函数,最先执行。

2.2 createState()

这是生命周期的起点。当Flutter框架需要创建一个StatefulWidget时,会立即调用createState()方法。这个方法必须返回一个State对象。

dart 复制代码
class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

2.3 initState()

在State对象创建后,框架会立即调用initState()方法。这是执行一次性初始化操作的理想位置:

  • 初始化依赖于BuildContext的数据
  • 订阅Stream或ChangeNotifier
  • 执行异步初始化(但要注意处理Future)
  • 设置定时器或动画控制器
dart 复制代码
class _MyWidgetState extends State<MyWidget> {
  late AnimationController _controller;
  StreamSubscription? _subscription;
  
  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    );
    
    // 订阅数据变化
    _subscription = myStream.listen((data) {
      setState(() {
        // 更新状态
      });
    });
  }
}

重要提示:必须调用super.initState(),否则会抛出异常。

2.4 didChangeDependencies()

手机挂载:initState方法之后,didChangeDependencies方法被调用。除此之外,当依赖的 InheritedWidget 发生变化的时候,didChangeDependencies也会被调用(可以多次被调用)。

dart 复制代码
@override
void didChangeDependencies() {
  super.didChangeDependencies();
  // 当依赖的InheritedWidget变化时,这里会被调用
  final theme = Theme.of(context);
  // 根据主题更新状态
}

2.5 build()

这是必须实现的方法,用于描述Widget如何根据当前状态进行渲染。build()方法应该是纯函数,只依赖于State的属性和传入的BuildContext。

dart 复制代码
@override
Widget build(BuildContext context) {
  return Container(
    color: Colors.blue,
    child: Text(
      'Count: $_count',
      style: TextStyle(fontSize: 24),
    ),
  );
}

2.6 创建阶段的生命周期方法验证

1.初始化的生命周期函数验证

我们以定时器的代码为例,在这个StatefulWidget中,我们都在控制台打印一下各个函数的名称。

Dart 复制代码
import 'dart:math';

import 'package:flutter/material.dart';

/// 生命周期示例页面。
/// 页面 State 持有两个可切换的颜色参数,点击按钮 setState 切换,
/// 将新颜色传给子组件 _ColorPanel,从而触发子组件的 didUpdateWidget。
class ChangeThemeExamplePage extends StatefulWidget {
  final String title;
  ChangeThemeExamplePage({super.key, required this.title}) {
    // widget 构造函数:创建 widget 配置对象,最先执行
    debugPrint('[widget 构造函数] 创建 widget 配置对象');
  }

  @override
  // ignore: no_logic_in_create_state - 本页面用于演示生命周期调用顺序,日志为有意为之
  State<ChangeThemeExamplePage> createState() {
    debugPrint('[createState] 创建 State 对象(widget 构造函数之后、State 构造函数之前)');
    return _ChangeThemeExamplePageState();
  }
}

class _ChangeThemeExamplePageState extends State<ChangeThemeExamplePage> {
  int _count = 0;

  /// 当前主题种子色,随机更新后整个页面主题随之变化
  Color _seedColor = Colors.deepPurple;

  final Random _random = Random();

  /// 随机生成一个不透明颜色作为新的主题种子色
  Color _randomColor() => Color(_random.nextInt(0xFFFFFF) | 0xFF000000);

  void _record(String method, String message) {
    debugPrint('[$method] $message');
    if (mounted) setState(() {});
  }

  @override
  void initState() {
    super.initState();
    _record('initState', 'State 对象创建,仅调用一次,适合初始化数据');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _record('didChangeDependencies', '依赖变化时调用(首次 build 前也会执行一次)');
  }

  void _increment() {
    setState(() {
      _count++;
    });
    debugPrint('[setState] 触发 rebuild,count=$_count');
  }

  /// 点击按钮随机更新主题:随机生成新种子色 → setState → 页面主题重建
  void _randomizeTheme() {
    setState(() {
      _seedColor = _randomColor();
      debugPrint('[setState] 随机更新主题 → #${_seedColor.toARGB32().toRadixString(16)}');
    });
  }

  @override
  void deactivate() {
    super.deactivate();
    debugPrint('[deactivate] 方法执行');
  }

  @override
  void dispose() {
    debugPrint('[dispose] 页面销毁,释放资源');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    debugPrint('[build] 方法执行');

    // 用 Theme 包裹整个页面:种子色变化时,页面内所有组件主题随之更新。
    // Builder 使内部 context 获取到新主题。
    return Theme(
      data: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: _seedColor),
      ),
      child: Builder(
        builder: (context) => Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: Text(widget.title),
          ),
          body: ListView(
            padding: const EdgeInsets.all(16),
            children: [
              const SizedBox(height: 16),
              // 计数器卡片
              Center(
                child:Text('当前按钮点击次数:$_count',style: Theme.of(context).textTheme.bodyMedium) ,
              ),
              const SizedBox(height: 4),
              ElevatedButton(onPressed: (){
                setState(() {
                  _count++;
                });
              }, child: const Text('计数器加一')),
            ],
          ),
        ),
      ),
    );
  }
}

此时,控制台打印日志如下:

flutter: widget 构造函数 创建 widget 配置对象

flutter: createState 创建 State 对象(widget 构造函数之后、State 构造函数之前)

flutter: initState State 对象创建,仅调用一次,适合初始化数据

flutter: didChangeDependencies 依赖变化时调用(首次 build 前也会执行一次)

flutter: build 方法执行

2.didChangeDependencies方法验证

当依赖的InheritedWidget发生变化的时候,会调用didChangeDependencies方法。常见的触发场景:

1.系统切换深色/浅色主题(Theme 变)

2.旋转屏幕 / 分屏(MediaQuery 变)

3.切换 App 语言(Localizations 变)

4.自定义 InheritedWidget(如自写 UserInfo.of(context))数据更新

还以上述的代码为例,我们如果想要修改系统深色和浅色。我们新增一个按钮,点击按钮,切换系统深色和浅色。

控制台打印日志如下:

flutter: setState 切换深浅色模式 → 浅色

flutter: build 方法执行

flutter: child.didChangeDependencies 第 5 次:局部 Theme 变化(主题色 Color(alpha: 1.0000, red: 0.4078, green: 0.3294, blue: 0.5569, colorSpace: ColorSpace.sRGB))

当我们点击刷新当前页面时候,通过手动调用setState方法之后,会自动调用build方法刷新页面。

完整代码如下:

Dart 复制代码
import 'dart:math';

import 'package:flutter/material.dart';

/// 主题切换示例页面。
///
/// 页面 State 持有「主题种子色」与「深浅色模式」两个状态:
/// - 随机更新主题:重新生成种子色,整页配色随之变化;
/// - 切换系统深色/浅色:切换明暗模式,观察组件配色自动适配;
/// - 计数器加一:触发页面重建,验证 build 重新执行。
///
/// 同时通过控制台日志展示 StatefulWidget 生命周期调用顺序。
class ChangeThemeExamplePage extends StatefulWidget {
  final String title;
  ChangeThemeExamplePage({super.key, required this.title}) {
    // widget 构造函数:创建 widget 配置对象,最先执行
    debugPrint('[widget 构造函数] 创建 widget 配置对象');
  }

  @override
  // ignore: no_logic_in_create_state - 本页面用于演示生命周期调用顺序,日志为有意为之
  State<ChangeThemeExamplePage> createState() {
    debugPrint('[createState] 创建 State 对象(widget 构造函数之后、State 构造函数之前)');
    return _ChangeThemeExamplePageState();
  }
}

class _ChangeThemeExamplePageState extends State<ChangeThemeExamplePage> {
  int _count = 0;

  /// 当前主题种子色,随机更新后整个页面主题随之变化
  Color _seedColor = Colors.deepPurple;

  /// 是否深色模式
  bool _darkMode = false;

  final Random _random = Random();

  /// 随机生成一个鲜艳的不透明颜色作为新的主题种子色
  Color _randomColor() {
    final hue = _random.nextDouble() * 360;
    return HSVColor.fromAHSV(1, hue, 0.72, 0.9).toColor();
  }

  void _record(String method, String message) {
    debugPrint('[$method] $message');
    if (mounted) setState(() {});
  }

  @override
  void initState() {
    super.initState();
    _record('initState', 'State 对象创建,仅调用一次,适合初始化数据');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _record('didChangeDependencies', '依赖变化时调用(首次 build 前也会执行一次)');
  }

  void _increment() {
    setState(() {
      _count++;
    });
    debugPrint('[setState] 触发 rebuild,count=$_count');
  }

  /// 随机更新主题:随机生成新种子色 → setState → 页面主题重建
  void _randomizeTheme() {
    setState(() {
      _seedColor = _randomColor();
      debugPrint(
          '[setState] 随机更新主题 → #${_seedColor.toARGB32().toRadixString(16)}');
    });
  }

  /// 切换深浅色模式:ThemeData 的 brightness 变化 → 页面主题重建
  void _toggleDarkMode() {
    setState(() {
      _darkMode = !_darkMode;
      debugPrint('[setState] 切换深浅色模式 → ${_darkMode ? '深色' : '浅色'}');
    });
  }

  @override
  void deactivate() {
    super.deactivate();
    debugPrint('[deactivate] 方法执行');
  }

  @override
  void dispose() {
    debugPrint('[dispose] 页面销毁,释放资源');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    debugPrint('[build] 方法执行');

    // 用 Theme 包裹整个页面:种子色 / 深浅色变化时,页面内所有组件主题随之更新。
    // Builder 使内部 context 获取到新主题。
    return Theme(
      data: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: _seedColor,
          brightness: _darkMode ? Brightness.dark : Brightness.light,
        ),
      ),
      child: Builder(
        builder: (context) => Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: Text(widget.title),
          ),
          body: ListView(
            padding: const EdgeInsets.all(16),
            children: [
              _buildThemeCard(context),
              const SizedBox(height: 16),
              _buildThemeControls(context),
              const SizedBox(height: 16),
              const _ThemeAwareChild(),
              const SizedBox(height: 16),
              _buildCounterCard(context),
              const SizedBox(height: 16),
              _buildHintCard(context),
            ],
          ),
        ),
      ),
    );
  }

  /// 当前主题展示卡:种子色色块 + 色值 + 明暗模式
  Widget _buildThemeCard(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [scheme.primary, scheme.tertiary],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(16),
      ),
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              const Icon(Icons.palette, color: Colors.white, size: 22),
              const SizedBox(width: 8),
              Text(
                '当前主题',
                style: Theme.of(context).textTheme.titleMedium?.copyWith(
                    color: Colors.white, fontWeight: FontWeight.bold),
              ),
            ],
          ),
          const SizedBox(height: 12),
          Row(
            children: [
              Container(
                width: 56,
                height: 56,
                decoration: BoxDecoration(
                  color: _seedColor,
                  shape: BoxShape.circle,
                  border: Border.all(color: Colors.white70, width: 2),
                ),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '种子色 #${_seedColor.toARGB32().toRadixString(16).substring(2).toUpperCase()}',
                      style: const TextStyle(
                          color: Colors.white,
                          fontSize: 13,
                          fontWeight: FontWeight.w600),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      '当前模式:${_darkMode ? '深色' : '浅色'}',
                      style: const TextStyle(color: Colors.white70),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }

  /// 主题操作按钮
  Widget _buildThemeControls(BuildContext context) {
    return Column(
      children: [
        SizedBox(
          width: double.infinity,
          child: FilledButton.icon(
            onPressed: _randomizeTheme,
            icon: const Icon(Icons.shuffle),
            label: const Text('随机更新主题'),
          ),
        ),
        const SizedBox(height: 8),
        SizedBox(
          width: double.infinity,
          child: OutlinedButton.icon(
            onPressed: _toggleDarkMode,
            icon: Icon(_darkMode ? Icons.light_mode : Icons.dark_mode),
            label: const Text('切换系统深色/浅色'),
          ),
        ),
      ],
    );
  }

  /// 计数器卡
  Widget _buildCounterCard(BuildContext context) {
    return Card(
      elevation: 0,
      color: Theme.of(context).colorScheme.surfaceContainerHighest,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('计数器', style: Theme.of(context).textTheme.titleSmall),
            const SizedBox(height: 8),
            Center(
              child: Text(
                '当前按钮点击次数:$_count',
                style: Theme.of(context).textTheme.bodyMedium,
              ),
            ),
            const SizedBox(height: 12),
            Center(
              child: FilledButton.icon(
                onPressed: _increment,
                icon: const Icon(Icons.add),
                label: const Text('计数器加一'),
              ),
            ),
          ],
        ),
      ),
    );
  }

  /// 操作说明卡
  Widget _buildHintCard(BuildContext context) {
    return Card(
      elevation: 0,
      color: Theme.of(context).colorScheme.surfaceContainerHighest,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: const Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('操作说明', style: TextStyle(fontWeight: FontWeight.bold)),
            SizedBox(height: 8),
            Text('· 随机更新主题:重新生成种子色,整页配色随之变化'),
            SizedBox(height: 4),
            Text('· 切换系统深色/浅色:切换明暗模式,组件配色自动适配'),
            SizedBox(height: 4),
            Text('· 计数器加一:触发页面重建,观察 build 重新执行'),
            SizedBox(height: 4),
            Text('· 主题变化时,位于局部 Theme 之下的子组件会触发 didChangeDependencies'),
          ],
        ),
      ),
    );
  }
}

/// 位于局部 Theme 之下的子组件:build 中调用 Theme.of(context) 注册依赖,
/// 主题(种子色 / 深浅色)变化时,didChangeDependencies 会被重新调用。
class _ThemeAwareChild extends StatefulWidget {
  const _ThemeAwareChild();

  @override
  State<_ThemeAwareChild> createState() => _ThemeAwareChildState();
}

class _ThemeAwareChildState extends State<_ThemeAwareChild> {
  int _dependencyCount = 0;
  int _buildCount = 0;

  @override
  void initState() {
    super.initState();
    debugPrint('[child.initState] 子组件挂载(尚未建立对局部 Theme 的依赖)');
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _dependencyCount++;
    // 局部 Theme 变化 → 本方法被重新调用。
    // 依赖变化后框架会自动重建本组件,无需手动 setState。
    final scheme = Theme.of(context).colorScheme;
    if (_dependencyCount == 1) {
      debugPrint('[child.didChangeDependencies] 第 1 次:首次挂载,initState 之后、build 之前');
    } else {
      debugPrint('[child.didChangeDependencies] 第 $_dependencyCount 次:局部 Theme 变化'
          '(主题色 ${scheme.primary})');
    }
  }

  @override
  Widget build(BuildContext context) {
    _buildCount++;
    final scheme = Theme.of(context).colorScheme; // 注册对局部 Theme 的依赖
    return Card(
      elevation: 0,
      color: scheme.primaryContainer.withValues(alpha: 0.3),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
        side: BorderSide(color: scheme.primary, width: 2),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            Icon(Icons.child_friendly, color: scheme.primary, size: 32),
            const SizedBox(width: 12),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('依赖局部 Theme 的子组件',
                      style: Theme.of(context).textTheme.titleSmall),
                  const SizedBox(height: 2),
                  Text(
                    '主题主色 #${scheme.primary.toARGB32().toRadixString(16).substring(2).toUpperCase()}',
                    style: Theme.of(context)
                        .textTheme
                        .bodySmall
                        ?.copyWith(color: scheme.primary),
                  ),
                  const SizedBox(height: 2),
                  Text(
                    'didChangeDependencies × $_dependencyCount · build × $_buildCount',
                    style: Theme.of(context).textTheme.bodySmall,
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

3. 更新阶段(Update Phase)

当Widget需要更新时(无论是内部状态变化还是父Widget重建),会进入更新阶段。

3.1 didUpdateWidget()

当父Widget重建并传入新的Widget配置时,框架会调用didUpdateWidget()。这是比较新旧Widget属性并相应更新State的好地方。

还以上述的代码为例,当我们点击切换颜色按钮的时候,当前Widget的颜色属性发生了变化,就会didUpdateWidget方法。

大致的逻辑如下:

父 Widget 重建 → 给同一个 Element 传入一个新的 Widget 实例

→ Element 发现 widget 变了 → 调 didUpdateWidget(oldWidget)

我们可以在源码中得到验证:

// framework.dart → StatefulElement.update()

@override

void update(StatefulWidget newWidget) {

final oldWidget = state._widget;

state._widget = newWidget; // ① 先换新引用

state.didUpdateWidget(oldWidget); // ② 再通知 State

super.update(newWidget); // ③ 里面标脏 → build()

}

所以调用顺序是:

didUpdateWidget(old) → build()

3.2 setState()

这是触发UI更新的关键方法。当State中的数据发生变化时,调用setState()通知框架需要重新构建Widget。

dart 复制代码
void _incrementCounter() {
  setState(() {
    _count++;
  });
}

最佳实践

  • 只在状态确实改变时调用setState()
  • 避免在build()方法中调用setState()
  • setState()回调应该是同步的

3.3 build()(再次调用)

在setState()、didUpdateWidget()或didChangeDependencies()之后,框架会再次调用build()方法来重建UI。

3.4 代码验证

1.页面刷新没有修改父类Widget属性的时候

还以上面的计时器demo为例,当我们的点击按钮的时候,会依次调用setState、build方法。

2.didUpdateWidget方法验证

4. 销毁阶段(Disposal Phase)

当Widget从Widget树中永久移除时,进入销毁阶段。

4.1 deactivate()

当State对象从树中移除时调用,但可能会被重新插入。这是清理临时资源的好时机。

dart 复制代码
@override
void deactivate() {
  // 暂停动画或计时器
  _controller.stop();
  super.deactivate();
}

4.2 dispose()

当State对象被永久移除时调用。必须在这里释放所有占用的资源,否则会导致内存泄漏。

dart 复制代码
@override
void dispose() {
  // 取消订阅
  _subscription?.cancel();
  
  // 释放动画控制器
  _controller.dispose();
  
  // 关闭数据库连接等
  _database.close();
  
  super.dispose();
}

关键要点

  • dispose()只会被调用一次
  • 必须调用super.dispose()
  • 确保释放所有资源:Stream订阅、动画控制器、定时器等

4.3 代码验证

当我们退出当前页面的时候,控制台打印日志如下:

flutter: deactivate 方法执行

flutter: dispose 页面销毁,释放资源

5. 生命周期方法调用顺序总结

阶段 方法 调用时机 常见用途
创建 contructor 创建Widget首先调用构造函数 创建StatefulWidget首先调用
创建 createState() Widget首次创建时 创建State实例
创建 initState() State初始化时 一次性初始化、订阅
创建 didChangeDependencies() initState后或依赖变化时 依赖InheritedWidget的初始化
创建/更新 build() 需要渲染UI时 构建Widget树
更新 didUpdateWidget() Widget配置更新时 比较新旧Widget属性
更新 setState() 需要更新UI时 通知框架状态变化
销毁 deactivate() 从树中移除但可能重新插入 暂停资源
销毁 dispose() 永久移除时 释放所有资源

6. 常见问题与最佳实践

6.1 避免在build()中执行耗时操作

build()方法应该快速执行,只负责构建UI。将耗时操作移到initState()或使用FutureBuilder/StreamBuilder。

6.2 正确处理异步操作

dart 复制代码
// 错误做法:在initState中直接await
@override
void initState() {
  super.initState();
  // 错误:build()会在数据加载完成前调用
  _loadData(); // 异步方法
}

// 正确做法:使用Future或Stream
Future<void> _loadData() async {
  final data = await api.fetchData();
  if (mounted) {
    setState(() {
      _data = data;
    });
  }
}

6.3 使用mounted属性

在异步回调中更新状态前,总是检查mounted属性,避免在dispose()后调用setState()。

6.4 生命周期方法调用顺序的确定性

Flutter保证生命周期方法的调用顺序是确定的,这有助于编写可预测的代码。

7. 调试技巧

  • 在initState()、dispose()等方法中添加print语句,观察调用顺序
  • 使用Flutter DevTools的Widget Inspector查看Widget树
  • 使用Performance Overlay检查build()方法的执行频率
  • 通过Hot Reload观察生命周期方法的变化

8. 总结

StatefulWidget的生命周期是Flutter框架的核心概念之一。深入理解每个阶段的用途和最佳实践,能够帮助开发者:

  1. 编写更高效、更稳定的Flutter应用
  2. 避免常见的内存泄漏和性能问题
  3. 更好地管理应用状态
  4. 优化应用的启动速度和响应性

记住,良好的生命周期管理是构建高质量Flutter应用的基础。在实际开发中,应该根据具体需求选择合适的状态管理方案(如Provider、Riverpod、Bloc等),这些方案都是在StatefulWidget生命周期基础上构建的。

通过掌握StatefulWidget的生命周期,您将能够编写出更加健壮、可维护的Flutter应用,为用户提供更好的体验。

相关推荐
xiangxiongfly9153 小时前
Flutter video_player总结
flutter·video_player·chewie
见山是山-见水是水20 小时前
鸿蒙版 Flutter Video 视频播放组件:播放控制、全屏切换与倍速播放
flutter·音视频·harmonyos
六年码农1 天前
2026最新开源 屏译ScreenTranslator 0.4.4 开源 全屏实时翻译教程
运维·人工智能·flutter·开源
WeiAreYoung1 天前
Flutter+iOS 实现 Live Activity
flutter·ios
恋猫de小郭1 天前
超好用 R8 Configuration Analyzer, 优化你的 App 大小和内存
android·前端·flutter
键盘飞行员2 天前
Flutter App 全套实战学习计划:从零搭建到 APK 部署
学习·flutter
GitLqr2 天前
Flutter 实战:图片上传前的压缩优化,解决带宽与存储的“大户”问题
flutter·全栈·dart
m0_749690232 天前
【寻迹校园 HarmonyOS NEXT 实战 07】不引入全局状态库:用 dataRevision 实现跨页面刷新
harmonyos·arkts·软件架构·状态管理·arkui
进哥AI研习社2 天前
AtomCode 跨端开发实战:Flutter 项目从原型到多端运行的 AI 驱动体验
人工智能·flutter