Flutter依赖注入与状态管理插件katana_scoped的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter依赖注入与状态管理插件katana_scoped的使用

介绍

Flutter的状态分为以下两类,如官方描述:

  • Ephemeral state:在单个widget内封闭的状态。例如导航栏的当前位置、文本表单的当前输入状态等。
  • App state:在应用内共享的状态。用于多个Widgets之间,例如从数据库检索的数据、登录状态、用户偏好设置等。

katana_scoped 是一个Flutter的状态管理插件,它通过显式地划分这些状态类型来简化状态管理。

安装

首先,您需要导入 katana_scoped 包:

flutter pub add katana_scoped

实现

前期准备

在应用程序根目录附近创建一个 AppRef 并放置 AppScoped 小部件。

// main.dart
import 'package:flutter/material.dart';
import 'package:katana_scoped/katana_scoped.dart';

void main() {
  runApp(const MyApp());
}

final appRef = AppRef();

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return AppScoped(
      appRef: appRef,
      child: MaterialApp(
        home: const CounterPage(),
        title: "Flutter Demo",
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
      ),
    );
  }
}

创建页面

创建页面时,实现一个扩展 PageScopedWidget 的小部件。

// counter_page.dart
import 'package:flutter/material.dart';
import 'package:katana_scoped/katana_scoped.dart';

class CounterPage extends PageScopedWidget {
  const CounterPage({super.key});

  [@override](/user/override)
  Widget build(BuildContext context, PageRef ref) {
    final counter = ref.page.watch((ref) => ValueNotifier(0));

    return Scaffold(
      appBar: AppBar(
        title: const Text("Test App"),
      ),
      body: Center(
        child: Text("${counter.value}"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.value++;
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

创建小部件

在页面下可以放置任何小部件,如 StatelessWidgetStatefulWidget,但如果您想管理状态,可以通过创建 ScopedWidget 来实现。

// scoped_test_page.dart
import 'package:flutter/material.dart';
import 'package:katana_scoped/katana_scoped.dart';

class ScopedTestPage extends PageScopedWidget {
  const ScopedTestPage({super.key});

  [@override](/user/override)
  Widget build(BuildContext context, PageRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Test App"),
      ),
      body: ScopedTestContent(),
    );
  }
}

class ScopedTestContent extends ScopedWidget {
  const ScopedTestContent({
    super.key,
  });

  [@override](/user/override)
  Widget build(BuildContext context, WidgetRef ref) {
    final widget = ref.widget.watch((ref) => ValueNotifier(0));
    final page = ref.page.watch((ref) => ValueNotifier(0));
    final app = ref.app.watch((ref) => ValueNotifier(0));

    return Column(
      children: [
        ListTile(
          title: Text(app.value.toString()),
          onTap: () {
            app.value++;
          },
        ),
        ListTile(
          title: Text(page.value.toString()),
          onTap: () {
            page.value++;
          },
        ),
        ListTile(
          title: Text(widget.value.toString()),
          onTap: () {
            widget.value++;
          },
        ),
      ],
    );
  }
}

使用示例代码

以下是完整的示例代码,展示了如何使用 katana_scoped 管理状态和依赖注入。

// main.dart
import 'package:flutter/material.dart';
import 'package:katana_scoped/katana_scoped.dart';

void main() {
  runApp(const MyApp());
}

final appRef = AppRef();

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return AppScoped(
      appRef: appRef,
      child: MaterialApp(
        home: const CounterPage(),
        title: "Flutter Demo",
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
      ),
    );
  }
}

final valueNotifierQuery =
    ChangeNotifierAppScopedQueryFamily<ValueNotifier<int>, int>(
  (ref, p) => ValueNotifier(p),
);

class CounterPage extends PageScopedWidget {
  const CounterPage({super.key});

  [@override](/user/override)
  Widget build(BuildContext context, PageRef ref) {
    final counter = ref.app.query(valueNotifierQuery(100));

    return Scaffold(
      appBar: AppBar(title: const Text("App Demo")),
      body: Center(
        child: Text("${counter.value}"),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counter.value++;
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

更多关于Flutter依赖注入与状态管理插件katana_scoped的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter依赖注入与状态管理插件katana_scoped的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用katana_scoped插件进行依赖注入与状态管理的代码示例。katana_scoped是一个轻量级的依赖注入和状态管理库,它允许你在Flutter应用中更轻松地管理依赖关系和状态。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加katana_scoped依赖:

dependencies:
  flutter:
    sdk: flutter
  katana_scoped: ^最新版本号 # 请替换为实际发布的最新版本号

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

2. 设置依赖注入容器

创建一个文件,比如app_container.dart,来设置你的依赖注入容器:

import 'package:katana_scoped/katana_scoped.dart';
import 'package:flutter/material.dart';

// 定义一个简单的服务类
class MyService {
  String getMessage() {
    return 'Hello from MyService!';
  }
}

// 创建依赖注入容器
final container = Container();

void setupLocator() {
  container.registerSingleton<MyService>(() => MyService());
}

3. 在应用入口初始化依赖注入

在你的main.dart文件中,初始化依赖注入容器:

import 'package:flutter/material.dart';
import 'app_container.dart';

void main() {
  // 初始化依赖注入
  setupLocator();

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScopedProvider<Container>(
      container: container,
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

4. 使用依赖注入和状态管理

在你的页面或组件中,你可以使用ScopedContainer来获取依赖:

import 'package:flutter/material.dart';
import 'package:katana_scoped/katana_scoped.dart';
import 'app_container.dart';

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final container = ScopedContainer.of<Container>(context);
    final myService = container.get<MyService>();

    return Scaffold(
      appBar: AppBar(
        title: Text('Katana Scoped Demo'),
      ),
      body: Center(
        child: Text(myService.getMessage()),
      ),
    );
  }
}

5. 复杂状态管理示例

如果你需要管理更复杂的状态,比如用户信息,你可以创建一个状态管理类并注册到容器中:

// user_state.dart
import 'package:katana_scoped/katana_scoped.dart';

class UserState {
  String? username;

  void setUsername(String username) {
    this.username = username;
  }
}

然后在app_container.dart中注册这个状态管理:

// app_container.dart
import 'package:katana_scoped/katana_scoped.dart';
import 'user_state.dart';

final container = Container();

void setupLocator() {
  container.registerSingleton<MyService>(() => MyService());
  container.registerSingleton<UserState>(() => UserState());
}

在你的页面中使用这个状态管理:

// my_home_page.dart
import 'package:flutter/material.dart';
import 'package:katana_scoped/katana_scoped.dart';
import 'app_container.dart';
import 'user_state.dart';

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final container = ScopedContainer.of<Container>(context);
    final userState = container.get<UserState>();
    final myService = container.get<MyService>();

    return Scaffold(
      appBar: AppBar(
        title: Text('Katana Scoped Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(myService.getMessage()),
            SizedBox(height: 20),
            Text('Username: ${userState.username ?? 'Not Set'}'),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                userState.setUsername('John Doe');
                setState(() {}); // 注意:由于MyHomePage是无状态的,这里不能直接调用setState。在真实场景中,你可能需要将状态提升到有状态组件。
              },
              child: Text('Set Username'),
            ),
          ],
        ),
      ),
    );
  }
}

注意:由于MyHomePage是无状态组件,它不能直接调用setState来刷新UI。在实际应用中,你可能需要将状态管理逻辑提升到有状态组件,或者使用ProviderGetX等更强大的状态管理库来处理UI刷新。这里仅作为演示katana_scoped的基本用法。

希望这个示例能帮助你理解如何在Flutter中使用katana_scoped进行依赖注入和状态管理。

回到顶部