在开发现代前端应用时,数据共享是不可避免的需求。无论是 React 中的 Redux,还是 Vue 中的 Vuex,都会面临数据在不同组件之间的传递与共享问题。对于刚接触 Flutter 的开发者来说,初始阶段可能会感到不适应,因为 Flutter 的编程语言和框架特性与这些主流前端框架有所不同。今天,我们来详细讨论一下 Flutter 中的数据共享方式,帮助大家更好地理解如何高效地在 Flutter 中管理全局状态和共享数据。

一、InheritedWidget

InheritedWidget 是 Flutter 中用于数据共享的一个基础组件,适用于在 Widget 树中共享数据。通过它,我们可以在应用的根 Widget 中共享一些数据,子 Widget 可以轻松获取并响应这些数据的变化。通常,InheritedWidget 用于跨多个 Widget 传递全局数据。

代码示例:
class ShareDataWidget extends InheritedWidget {
  final int data; // 需要共享的数据,例如点击次数

  ShareDataWidget({
    required this.data,
    required Widget child,
  }) : super(child: child);

  // 提供一个便捷方法来获取共享的数据
  static ShareDataWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
  }

  @override
  bool updateShouldNotify(ShareDataWidget old) {
    return old.data != data; // 如果数据变化,则通知依赖它的子 Widget 更新
  }
}

在上面的代码中,ShareDataWidget 继承自 InheritedWidget,通过 of 方法,子 Widget 可以方便地访问共享数据。当数据发生变化时,Flutter 会自动重新构建依赖该数据的子 Widget。
使用示例:
class TestWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(ShareDataWidget.of(context)!.data.toString());
  }
}

class InheritedWidgetRoute extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _InheritedWidgetRouteState();
}

class _InheritedWidgetRouteState extends State<InheritedWidgetRoute> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ShareDataWidget(
        data: count,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Padding(
              padding: const EdgeInsets.all(10),
              child: TestWidget(),
            ),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  count++;
                });
              },
              child: Text("点击+1"),
            ),
          ],
        ),
      ),
    );
  }
}

二、EventBus

EventBus 是一种用于组件间通信的模式。它类似于 Vue 中的事件总线,可以让 Flutter 应用中的不同页面或 Widget 之间传递事件而无需直接引用彼此。

代码示例:

 
class CustomEvent {
  final String msg;
  CustomEvent(this.msg);
}

EventBus eventBus = EventBus();

class FirstPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _FirstPageState();
}

class _FirstPageState extends State<FirstPage> {
  String msg = "通知:";
  late StreamSubscription subscription;

  @override
  void initState() {
    super.initState();
    subscription = eventBus.on<CustomEvent>().listen((event) {
      setState(() {
        msg += event.msg;
      });
    });
  }

  @override
  void dispose() {
    subscription.cancel(); // 清理订阅
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("First Page")),
      body: Center(child: Text(msg)),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => SecondPage()),
        ),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Second Page")),
      body: ElevatedButton(
        child: Text('触发事件'),
        onPressed: () => eventBus.fire(CustomEvent("Hello from Second Page!")),
      ),
    );
  }
}

三、Provider

Provider 是 Flutter 官方推荐的一种管理全局状态的方式,它基于 InheritedWidget 实现,但提供了更简洁的 API 和更强大的功能。通过 ChangeNotifierConsumer,我们可以在应用中轻松实现数据的响应式更新。

1. 安装 provider

首先,在 pubspec.yaml 文件中添加 provider 依赖:


 
dependencies:
  provider: ^6.0.0

然后运行 flutter pub get 安装依赖。

2. 配置 Provider

main.dart 文件中,我们使用 MultiProvider 来提供全局状态,ChangeNotifierProvider 用于将状态提供给整个应用。


 
void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => GlobalData()),
      ],
      child: MyApp(),
    ),
  );
}

class GlobalData with ChangeNotifier {
  bool _hasLogin = false;

  bool get hasLogin => _hasLogin;

  void login() {
    _hasLogin = true;
    notifyListeners(); // 通知依赖此状态的 Widget 更新
  }

  void logout() {
    _hasLogin = false;
    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<GlobalData>(
      builder: (context, globalData, _) {
        return MaterialApp(
          home: globalData.hasLogin ? HomePage() : LoginPage(),
        );
      },
    );
  }
}

3. 使用 Provider 提供的数据

在需要获取或更新状态的地方,我们使用 Consumer 来监听状态的变化,并根据需要更新 UI:


 
class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Provider.of<GlobalData>(context, listen: false).login();
          },
          child: Text("Login"),
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Provider.of<GlobalData>(context, listen: false).logout();
          },
          child: Text("Logout"),
        ),
      ),
    );
  }
}

总结

在 Flutter 中,有多种方式可以实现数据共享,选择适合的方式可以大大简化应用的架构设计和状态管理:

  • InheritedWidget 适合用于轻量级的数据共享,尤其是在 Widget 树中传递数据。
  • EventBus 提供了一种松耦合的事件通信机制,适用于页面或 Widget 之间的消息传递。
  • Provider 是 Flutter 官方推荐的方式,适合管理全局状态,支持响应式更新,推荐在中大型项目中使用。

通过这些方式,我们可以轻松地实现 Flutter 中的数据共享和状态管理,提升开发效率和应用性能。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐