Flutter 开发之第一个Flutter应用

1. 在flutter中实现类似Vue的自适应更新数据,而不是每次都需要setState的方式

方案一:Riverpod(目前最主流)

Riverpod 是 Provider 作者的新作,2023-2024 年火起来的。它虽然还是要调用方法更新,但配合 ref.watch 可以实现类似自动响应的体验:

// 复制代码
final counterProvider = StateProvider((ref) => 0);

// 在 widget 中使用
class CounterWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider); // 自动监听,变化时重建
    return Text('$count');
  }
}

Riverpod 的特点是编译安全、不依赖 BuildContext、支持自动销毁状态

csharp 复制代码
void main() {
  runApp(
    // 添加这个ProviderScope
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

ProviderScope 就像一个全局的"状态容器",它负责存储和管理我们接下来要创建的所有Provider的状态,之所以写在根组件上是保证所有他的子组件可以共享一个状态,类似于单例,当然也可以不写,而写在其他地方,但要注意,这样的话会导致平级或者上级无法响应数据变化,一旦销毁了,子组件之间的关系也解除了。

如果要监听的数据很多,可以包装成一个对象,单独监听这个对象或者这个对象的某个属性。

kotlin 复制代码
// 定义一个用户信息类
class UserProfile {
  UserProfile({this.name = '', this.age = 0, this.email = ''});
  String name;
  int age;
  String email;
}

// 用一个 provider 管理整个用户信息
final userProfileProvider = StateProvider<UserProfile>((ref) => UserProfile());
ini 复制代码
final name = ref.watch(userProfileProvider.select((user) => user.name));

方案二:GetX(极简风格)

class 复制代码
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    // return MaterialApp(
    //   title: 'Flutter Demo',
    //   theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
    //   home: const MyHomePage(title: 'Flutter Demo Home Page1223'),
    // );
    // 将 MaterialApp 替换为 GetMaterialApp
    return GetMaterialApp(
      title: 'Flutter GetX Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomePage(), // 指向我们的新首页
    );
  }
}
1.为什么在顶层要用GetMaterialApp? 如果不替换,GetX 的路由和全局交互功能可能无法正常工作。但如果你只使用状态管理(Obx.obs),不涉及路由和对话框,理论上普通 MaterialApp 也能运行(因为 Obx 依赖于 Get 的上下文,而 Get 可以通过其他方式初始化)。不过官方强烈建议替换,以享受完整的 GetX 生态。
2. 为什么是 0.obs?它背后是什么?

0.obs 是 GetX 提供的一种简洁的响应式变量创建方式 。它的本质是 Dart 扩展方法 + 响应式包装类

  • .obs 是 GetX 为 Dart 的基础类型(如 intStringboolList 等)添加的扩展方法。当你写 0.obs 时,实际上调用的是 RxInt(0),它返回一个 RxInt 对象。
  • RxInt 继承自 Rx<T>,内部维护了一个值(_value)和一个订阅者列表(_observers)。
  • 当你读取 .value 时,RxInt 会记录当前正在运行的 Obx 闭包(如果有的话),将其添加到订阅者列表中。
  • 当你修改 .value 时(比如 count.value++),RxInt 会遍历所有订阅者,通知它们重新执行闭包,从而更新 UI。

这种设计被称为响应式编程 (类似 Vue 的 ref 或 MobX 的 observable),通过 .obs 让普通变量变得可观察。


3. Obx(() => Text('${controller.count.value}'))setState 真的是一样的吗?

完全不同! 虽然写起来确实需要明确告诉框架"我要监听 count",但背后的机制和性能表现截然不同:

相同点

  • 都需要"标记"哪些数据会影响 UI(在 GetX 里是 .obs 变量,在 setState 里是整个 State 的成员变量)。
  • 都是响应式的------数据变化驱动 UI 更新。

方案三:信号机制(Signals)- 2025 年新趋势

尚未有稳定版本推出,dart版本需要3.5以上,目前最新的flutter稳定版本不支持

方案四:Bloc(事件驱动)

2. 关于Widget

state
获取state的方式一:通过Context获取

context对象有一个findAncestorStateOfType()方法,该方法可以从当前节点沿着 widget 树向上查找指定类型的 StatefulWidget 对应的 State 对象。

context

在子树中获取父级 widget 的一个示例:

scala 复制代码
class ContextRoute extends StatelessWidget  {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Context测试"),
      ),
      body: Container(
        child: Builder(builder: (context) {
          // 在 widget 树中向上查找最近的父级`Scaffold`  widget 
          Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
          // 直接返回 AppBar的title, 此处实际上是Text("Context测试")
          return (scaffold.appBar as AppBar).title;
        }),
      ),
    );
  }
}
  • 从当前 Element 开始,通过 parent 指针依次访问每一个祖先 Element
  • 对于每个祖先 Element,检查它是否是 StatefulElement(即对应的 widgetStatefulWidget)。
  • 如果是 StatefulElement,再检查其 widget 的类型是否与传入的类型参数 T 匹配。
  • 如果匹配,则返回该 StatefulElementstate 属性(即 State 对象)。
  • 如果遍历到根节点仍未找到,返回 null
通过GlobalKey
  1. 给目标StatefulWidget添加GlobalKey

    scss 复制代码
    //定义一个globalKey, 由于GlobalKey要保持全局唯一性,我们使用静态变量存储
    static GlobalKey<ScaffoldState> _globalKey= GlobalKey();
    ...
    Scaffold(
        key: _globalKey , //设置key
        ...  
    )
  2. 通过GlobalKey来获取State对象

    scss 复制代码
    _globalKey.currentState.openDrawer()

GlobalKey 是 Flutter 提供的一种在整个 App 中引用 element 的机制。如果一个 widget 设置了GlobalKey,那么我们便可以通过globalKey.currentWidget获得该 widget 对象、globalKey.currentElement来获得 widget 对应的element对象,如果当前 widget 是StatefulWidget,则可以通过globalKey.currentState来获得该 widget 对应的state对象。

注意:使用 GlobalKey 开销较大,如果有其他可选方案,应尽量避免使用它。另外,同一个 GlobalKey 在整个 widget 树中必须是唯一的,不能重复。

相关推荐
尘中客3 小时前
放弃 Echarts?前端直接渲染后端高精度 SVG 矢量图流的踩坑记录
前端·javascript·echarts·前端开发·svg矢量图·echarts避坑
FreeBuf_3 小时前
Chrome 0Day漏洞遭野外利用
前端·chrome
小彭努力中4 小时前
199.Vue3 + OpenLayers 实现:点击 / 拖动地图播放音频
前端·vue.js·音视频·openlayers·animate
2501_916007474 小时前
网站爬虫原理,基于浏览器点击行为还原可接口请求
前端·javascript·爬虫·ios·小程序·uni-app·iphone
前端大波4 小时前
Sentry 每日错误巡检自动化:设计思路与上手实战
前端·自动化·sentry
ZC跨境爬虫5 小时前
使用Claude Code开发校园交友平台前端UI全记录(含架构、坑点、登录逻辑及算法)
前端·ui·架构
慧一居士5 小时前
Vue项目中,何时使用布局、子组件嵌套、插槽 对应的使用场景,和完整的使用示例
前端·vue.js
Можно5 小时前
uni.request 和 axios 的区别?前端请求库全面对比
前端·uni-app
M ? A6 小时前
解决 VuReact 中 ESLint 规则冲突的完整指南
前端·react.js·前端框架
Jave21086 小时前
实现全局自定义loading指令
前端·vue.js