Flutter未知功能插件jetpack的使用(注:由于介绍为undefined,故假设为未知功能)

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

Flutter未知功能插件Jetpack的使用

Jetpack for Flutter

Jetpack for Flutter是一组从Android Jetpack中汲取灵感的抽象和工具,旨在帮助管理Flutter应用程序中的状态。

功能

LiveData

作为状态持有者和更改通知器,还允许读取当前值。如果你完全依赖于Stream和响应式编程,可能不需要这个特性。但如果你想通过命令式代码更新状态,这将有所帮助。

EventQueue

用于推送短暂的状态到UI并在处理后清除。对于从ViewModel内部触发吐司/弹出窗口非常有用。

ViewModel

业务逻辑容器,它暴露状态和事件方法给UI,并与应用程序的其余部分通信。

安装

$ flutter pub add jetpack

使用

创建你的ViewModel并使用LiveData公开状态:

import 'package:jetpack/jetpack.dart';

class CounterViewModel extends ViewModel {
  final MutableLiveData<int> _counter = MutableLiveData(0);

  LiveData<int> get counter => _counter;

  void increment() {
    _counter.value++;
  }
}

你可以通过BuildContext访问你的CounterViewModel

@override
Widget build(BuildContext context) {
  final CounterViewModel viewModel = context.viewModel();
}

并且你可以使用LiveDataBuilder消费LiveData

LiveDataBuilder<int>(
  liveData: viewModel.counter,
  builder: (BuildContext buildContext, int count) =>
      Text('$count'),
)

你还可以通过调用ViewModel上的方法传递UI事件:

FloatingActionButton(
  onPressed: viewModel.increment,
  //...
)

在Flutter项目中的一次性设置

创建一个ViewModelFactory为你的应用

class MyAppViewModelFactory extends ViewModelFactory {
  const MyAppViewModelFactory();

  @override
  T create<T extends ViewModel>() {
    if (T == HomeViewModel) {
      return HomeViewModel() as T;
    }
    throw Exception("Unknown ViewModel type");
  }
}

如果你使用依赖注入框架如get_it,你的工厂可以像这样:

class MyAppViewModelFactory extends ViewModelFactory {
  const MyAppViewModelFactory();

  @override
  T create<T extends ViewModel>() {
    return GetIt.I.get<T>();
  }
}

在你的App根部提供ViewModelFactory

void main() {
  const MyAppViewModelFactory viewModelFactory = MyAppViewModelFactory();
  runApp(const MyApp(viewModelFactory: viewModelFactory));
}

class MyApp extends StatelessWidget {
  final ViewModelFactory viewModelFactory;
  const MyApp({super.key, required this.viewModelFactory});

  @override
  Widget build(BuildContext context) {
    return ViewModelFactoryProvider(
        viewModelFactory: viewModelFactory,
        child: MaterialApp(
          title: 'Flutter App',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const HomePage(title: 'Home Page'),
        ));
  }
}

创建一个基础小部件Page来包装所有页面内容与ViewModelScope

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

  Widget buildContent(BuildContext context);

  @override
  Widget build(BuildContext context) {
    return ViewModelScope(builder: buildContent);
  }
}

如果你已经有所有页面的基础类,则按上述方式使用ViewModelScope包装内容。

为什么又一个状态管理库?

这些模式在Android生态系统中已经存在超过5年。即使在采用全新的UI框架——Jetpack Compose之后,它们仍然保持不变。这些抽象由于低耦合灵活性而具有强大的适应能力。

现有解决方案如blocprovider等默认情况下仅限于发出一个状态流,并且需要额外的样板代码来“选择”UI想要反应的状态片段。

有时候,我们希望暴露多个不同但相关联的状态流,这些流以不同的频率变化/发出。直接从ViewModel暴露它们,无需编写选择器等样板代码是非常方便的,没有任何成本。

这使我们能够组织和传播状态,以便按照其在UI中被消耗的方式进行最小化不必要的小部件重建。

你也可以使用FutureStream暴露状态给UI。你的选择

并且不需要为将UI事件传达给ViewModel创建额外的模型。只需直接调用方法即可。

示例代码

以下是一个完整的示例,展示了如何在一个简单的计数器应用中使用Jetpack插件:

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

// 创建ViewModel
class CounterViewModel extends ViewModel {
  final MutableLiveData<int> _counter = MutableLiveData(0);

  LiveData<int> get counter => _counter;

  void increment() {
    _counter.value++;
  }
}

// ViewModelFactory
class ExampleViewModelFactory extends ViewModelFactory {
  const ExampleViewModelFactory();

  @override
  T create<T extends ViewModel>() {
    if (T == CounterViewModel) {
      return CounterViewModel() as T;
    }
    throw Exception("Unknown ViewModel type");
  }
}

void main() {
  const ExampleViewModelFactory viewModelFactory = ExampleViewModelFactory();
  runApp(const MyApp(viewModelFactory: viewModelFactory));
}

class MyApp extends StatelessWidget {
  final ViewModelFactory viewModelFactory;
  const MyApp({super.key, required this.viewModelFactory});

  @override
  Widget build(BuildContext context) {
    return ViewModelFactoryProvider(
      viewModelFactory: viewModelFactory,
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const HomePage(title: 'Simple Scope Demo'),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key, required this.title});
  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: ViewModelScope(
          builder: (_) => Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Padding(
                padding: EdgeInsets.all(16),
                child: Text(
                    'Here are two counter components in the same scope. They react to clicks on each other because they are talking to the same ViewModel'),
              ),
              const Counter(),
              const Counter(),
              ElevatedButton(
                  onPressed: () {
                    Navigator.of(context).push(MaterialPageRoute(
                        builder: (context) => const MultiScopeDemo()));
                  },
                  child: const Text('NEXT'))
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final CounterViewModel viewModel = context.viewModel<CounterViewModel>();

    return Column(
      children: [
        LiveDataBuilder<int>(
          liveData: viewModel.counter,
          builder: (context, count) => Text('Count: $count'),
        ),
        FloatingActionButton(
          onPressed: viewModel.increment,
          child: const Icon(Icons.add),
        ),
      ],
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Multi Scope Demo'),
      ),
      body: Center(
        child: ViewModelScope(
          builder: (_) => const Text('This is a new scope.'),
        ),
      ),
    );
  }
}

这段代码展示了一个包含两个计数器组件的应用程序,它们在同一作用域内共享同一个ViewModel,因此会相互响应点击事件。此外,还有一个导航按钮可以跳转到新的作用域页面。


更多关于Flutter未知功能插件jetpack的使用(注:由于介绍为undefined,故假设为未知功能)的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter未知功能插件jetpack的使用(注:由于介绍为undefined,故假设为未知功能)的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,针对一个假设为“未知功能”的Flutter插件jetpack,虽然我们不能确切知道其具体功能,但我可以提供一个基本的Flutter插件集成和使用示例代码框架。这可以帮助你理解如何集成和使用一个假设的新插件。

首先,假设jetpack插件已经发布在pub.dev上,或者你已经从某个源获取到了它的包。以下是一个基本的集成和使用示例:

1. 添加依赖

在你的pubspec.yaml文件中添加jetpack依赖:

dependencies:
  flutter:
    sdk: flutter
  jetpack: ^x.y.z  # 假设版本号为x.y.z,根据实际情况替换

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

2. 导入插件

在你需要使用jetpack功能的Dart文件中导入插件:

import 'package:jetpack/jetpack.dart';

3. 初始化插件(如果需要)

有些插件可能需要在应用启动时进行初始化。假设jetpack插件有一个initialize方法:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Jetpack.initialize();  // 假设有一个初始化方法
  runApp(MyApp());
}

4. 使用插件功能

由于jetpack的具体功能未知,以下是一个假设的使用示例。假设它有一个performUnknownAction方法,该方法接受一些参数并返回一个结果:

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Jetpack.initialize();  // 假设初始化
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Jetpack Plugin Demo'),
        ),
        body: Center(
          child: JetpackButton(),
        ),
      ),
    );
  }
}

class JetpackButton extends StatefulWidget {
  @override
  _JetpackButtonState createState() => _JetpackButtonState();
}

class _JetpackButtonState extends State<JetpackButton> {
  String result = 'No result yet';

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async {
        try {
          // 假设performUnknownAction是一个返回String的方法
          String res = await Jetpack.performUnknownAction(param1: 'value1', param2: 123);
          setState(() {
            result = res;
          });
        } catch (e) {
          setState(() {
            result = 'Error: $e';
          });
        }
      },
      child: Text('Perform Unknown Action'),
    );
  }

  @override
  void initState() {
    super.initState();
    // 可以在这里进行任何需要的初始化
  }
}

5. 插件方法定义(假设)

虽然这不是你实际会写的代码,但为了完整性,假设jetpack插件的Dart代码可能看起来像这样(仅作为示例):

library jetpack;

import 'dart:async';

class Jetpack {
  static Future<void> initialize() async {
    // 初始化逻辑
  }

  static Future<String> performUnknownAction({required String param1, required int param2}) async {
    // 执行未知操作的逻辑
    // 这里只是返回一个假设的结果
    return 'Action performed with $param1 and $param2';
  }
}

请注意,上述代码中的Jetpack类和方法定义仅用于说明目的,并不是实际的插件代码。在实际使用中,你将依赖于插件作者提供的API文档和示例代码。

由于jetpack插件的具体功能和API未知,上述代码提供了一个基本的框架,你可以根据实际情况进行调整和扩展。

回到顶部