Flutter自定义业务逻辑插件custom_bloc的使用
Flutter自定义业务逻辑插件custom_bloc的使用
简介
custom_bloc
是一个基于 BLOC(Business Logic Component)模式的简单自定义流构建库,它使用底层的 Stream
来处理数据流。该库允许轻松地从网络添加数据,并且可以方便地管理 BLOC 的生命周期。
功能
- 支持从网络获取数据并将其添加到流中。
- 提供了便捷的方法来处理加载状态、错误状态和无数据状态。
- 支持多个流的组合使用。
- 支持无限滚动加载。
使用方法
1. 初始化项目
首先,确保你已经在 pubspec.yaml
文件中添加了 custom_bloc
依赖:
dependencies:
custom_bloc: ^latest_version
然后运行 flutter pub get
来安装依赖。
2. 创建 BLOC 类
CounterBloc
是一个简单的 BLOC 类,用于管理计数器的状态。它继承自 BaseBloc<int, String>
,其中 int
是数据类型,String
是错误信息类型。
class CounterBloc extends BaseBloc<int, String> {
int value = 0;
CounterBloc() {
fetchCurrent(false);
}
// 获取当前值
fetchCurrent(bool addError) async {
setAsLoading(); // 设置为加载状态
if (!addError) {
await Future.delayed(const Duration(seconds: 2)); // 模拟网络请求延迟
value++;
addToModel(value); // 将新值添加到流中
} else {
value = 0;
addToError('Could not fetch data'); // 添加错误信息
}
}
// 重置数据
resetData() {
value = 0;
setAsNoContent(); // 设置为无数据状态
}
// 无效化 BLOC
invalidate() {
invalidateBaseBloc();
}
// 释放资源
dispose() {
disposeBaseBloc();
}
}
3. 创建 UI 页面
接下来,我们创建一个简单的 UI 页面来展示 CounterBloc
的功能。我们将使用 CustomStreamBuilder
来监听 BLOC 的流,并根据不同的状态显示不同的内容。
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
[@override](/user/override)
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
final counterBloc = CounterBloc();
[@override](/user/override)
void dispose() {
super.dispose();
counterBloc.dispose(); // 释放 BLOC 资源
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: CustomStreamBuilder(
stream: counterBloc.behaviorSubject, // 监听 BLOC 的流
dataBuilder: (context, data) {
return ListView.separated(
itemCount: 1,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 24),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'index: $index: data: $data', // 显示数据
style: const TextStyle(fontSize: 34),
),
],
),
);
},
separatorBuilder: (context, index) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: VerticalDivider(),
);
},
);
},
loadingBuilder: (context) => const Center(
child: CircularProgressIndicator(), // 加载时显示进度条
),
errorBuilder: (context, error) => Text(
error, // 错误时显示错误信息
style: const TextStyle(),
),
),
),
const SizedBox(height: 34),
Row(
children: [
TextButton(
onPressed: () {
counterBloc.fetchCurrent(false); // 添加正常数据
},
child: const Text('Add Value'),
),
const SizedBox(width: 34),
TextButton(
onPressed: () {
counterBloc.resetData(); // 重置数据
},
child: const Text('Set to no data'),
),
const SizedBox(width: 34),
TextButton(
onPressed: () {
counterBloc.fetchCurrent(true); // 添加错误
},
child: const Text('Add Error'),
),
],
),
const SizedBox(height: 34),
],
),
),
);
}
}
4. 多个 BLOC 的组合使用
如果你有多个 BLOC 需要同时管理,可以使用 CustomStreamBuilder.twoSubject
或 CustomStreamBuilder.multiSubject
来组合多个流。
class Example2 extends StatefulWidget {
const Example2({Key? key}) : super(key: key);
[@override](/user/override)
State<Example2> createState() => _Example2State();
}
class _Example2State extends State<Example2> {
final counterBloc = CounterBloc();
final newCounterBloc = CounterBloc2();
[@override](/user/override)
void dispose() {
super.dispose();
counterBloc.dispose();
newCounterBloc.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: CustomStreamBuilder.twoSubject(
streams: [
counterBloc.behaviorSubject, // 第一个流
newCounterBloc.behaviorSubject // 第二个流
],
dataBuilder2: (context, data, secondData) {
var counterBlocData = data.model;
var counterBlocState = data.itemState;
var counterBlocDataError = data.error;
var newCounterBlocData = secondData.model;
var newCounterBlocState = secondData.itemState;
var newCounterBlocError = secondData.error;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (counterBlocState == ItemState.loading)
const Center(child: CircularProgressIndicator())
else if (counterBlocState == ItemState.hasError)
Text('counterBlocData error: $counterBlocDataError', style: const TextStyle(fontSize: 34))
else if (counterBlocState == ItemState.noContent)
const SizedBox()
else
Text('counterBlocData: $counterBlocData', style: const TextStyle(fontSize: 34)),
const SizedBox(height: 34),
if (newCounterBlocState == ItemState.loading)
const Center(child: CircularProgressIndicator())
else if (newCounterBlocState == ItemState.hasError)
Text('newCounterBlocError error: $newCounterBlocError', style: const TextStyle(fontSize: 34))
else if (newCounterBlocState == ItemState.noContent)
const SizedBox()
else
Text('newCounterBlocData: $newCounterBlocData', style: const TextStyle(fontSize: 34)),
],
),
);
},
loadingBuilder: (context) => const Center(child: CircularProgressIndicator()),
errorBuilder: (context, error) => Text(error.toString()),
),
),
const SizedBox(height: 34),
Wrap(
children: [
TextButton(onPressed: () => counterBloc.fetchCurrent(false), child: const Text('Add Value to stream 1')),
const SizedBox(width: 34),
TextButton(onPressed: () => counterBloc.resetData(), child: const Text('Set to no data stream 1')),
const SizedBox(width: 34),
TextButton(onPressed: () => counterBloc.fetchCurrent(true), child: const Text('Add Error to stream 1')),
const SizedBox(width: 34),
TextButton(onPressed: () => newCounterBloc.fetchCurrent(false), child: const Text('Add Value to stream 2', style: TextStyle(color: Colors.red))),
const SizedBox(width: 34),
TextButton(onPressed: () => newCounterBloc.resetData(), child: const Text('Set to no data stream 2', style: TextStyle(color: Colors.red))),
const SizedBox(width: 34),
TextButton(onPressed: () => newCounterBloc.fetchCurrent(true), child: const Text('Add Error to stream 2', style: TextStyle(color: Colors.red))),
],
),
const SizedBox(height: 34),
],
),
),
);
}
}
5. 无限滚动加载
CounterInfiniteScrollingBloc
是一个支持无限滚动加载的 BLOC 类。它继承自 BaseBlocInfiniteLoadingFactoryBloc<double, String>
,并且实现了 initFetch
和 fetchNextPage
方法来处理分页加载。
class CounterInfiniteScrollingBloc extends BaseBlocInfiniteLoadingFactoryBloc<double, String> {
int get page => pageNumber;
int get itemsPerPage => perPage;
double value = 0;
[@override](/user/override)
initFetch() async {
super.initFetch();
setAsLoading(); // 设置为加载状态
await Future.delayed(const Duration(seconds: 2)); // 模拟网络请求延迟
value += 0.5;
addToModel(value); // 将新值添加到流中
incrementPageCountAndStartLoaderSubject(); // 增加页码并启动加载器
}
[@override](/user/override)
fetchNextPage({Function()? onSuccessfulFetch, Function()? onFailedFetch}) async {
bool isSuccessful = await Future.delayed(const Duration(seconds: 2)); // 模拟网络请求延迟
if (isSuccessful) {
value += 0.5;
addToModel(value); // 将新值添加到流中
incrementPageCountAndStartLoaderSubject(); // 增加页码并启动加载器
} else {
stopLoaderSubject(); // 停止加载器
}
}
resetData() {
value = 0;
setAsNoContent(); // 设置为无数据状态
}
invalidate() {
invalidateBaseBloc();
}
dispose() {
disposeBaseBloc();
}
}
6. 无限滚动加载的 UI 实现
在 UI 中,我们可以使用 CustomInfiniteScrollingStreamBuilder
来实现无限滚动加载的功能。
class ExampleInfiniteScrolling extends StatefulWidget {
const ExampleInfiniteScrolling({Key? key}) : super(key: key);
[@override](/user/override)
State<ExampleInfiniteScrolling> createState() => _ExampleInfiniteScrollingState();
}
class _ExampleInfiniteScrollingState extends State<ExampleInfiniteScrolling> {
final counterBloc = CounterInfiniteScrollingBloc();
[@override](/user/override)
void dispose() {
super.dispose();
counterBloc.dispose(); // 释放 BLOC 资源
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: CustomInfiniteScrollingStreamBuilder(
loaderStream: counterBloc.loaderSubject, // 监听加载器流
builder: (context, loadingData) => CustomStreamBuilder(
stream: counterBloc.behaviorSubject, // 监听 BLOC 的流
dataBuilder: (context, data) {
return NotificationListener(
onNotification: (notification) {
if (notification is ScrollNotification) {
counterBloc.sink.add(notification); // 监听滚动事件
}
return true;
},
child: ListView.separated(
itemCount: 1,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 24),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'index: $index: data: $data', // 显示数据
style: const TextStyle(fontSize: 34),
),
],
),
);
},
separatorBuilder: (context, index) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: VerticalDivider(),
);
},
),
);
},
loadingBuilder: (context) => const Center(child: CircularProgressIndicator()), // 加载时显示进度条
errorBuilder: (context, error) => Text(error), // 错误时显示错误信息
),
),
),
const SizedBox(height: 34),
Row(
children: [
TextButton(onPressed: () => counterBloc.fetchNextPage(), child: const Text('Add Value')),
const SizedBox(width: 34),
TextButton(onPressed: () => counterBloc.resetData(), child: const Text('Set to no data')),
const SizedBox(width: 34),
TextButton(onPressed: () => counterBloc.fetchNextPage(), child: const Text('Add Error')),
],
),
const SizedBox(height: 34),
],
),
),
);
}
}
更多关于Flutter自定义业务逻辑插件custom_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter自定义业务逻辑插件custom_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中创建和使用自定义业务逻辑插件 custom_bloc
的示例。这个示例将展示如何定义一个简单的BLoC(Business Logic Component),并在Flutter应用中使用它。
1. 创建 custom_bloc
插件
首先,我们需要创建一个Flutter插件项目。假设你已经在你的开发环境中安装并配置好了Flutter SDK。
创建插件项目
flutter create --template=plugin custom_bloc
定义BLoC逻辑
进入插件项目目录,并在 lib
文件夹中创建一个新的 Dart 文件,例如 counter_bloc.dart
。
// lib/counter_bloc.dart
import 'dart:async';
abstract class CounterEvent {}
class IncrementEvent implements CounterEvent {}
class DecrementEvent implements CounterEvent {}
class CounterState {
final int count;
CounterState(this.count);
}
class CounterBloc {
CounterBloc() : _controller = StreamController<CounterState>.broadcast() {
_counter = 0;
_subscription = _eventController.stream.listen(_mapEventToState);
}
final _eventController = StreamController<CounterEvent>.broadcast();
final _controller = StreamController<CounterState>.broadcast();
int _counter;
StreamSubscription<CounterEvent> _subscription;
Stream<CounterState> get state => _controller.stream;
void dispatch(CounterEvent event) {
_eventController.add(event);
}
void _mapEventToState(CounterEvent event) {
if (event is IncrementEvent) {
_counter++;
} else if (event is DecrementEvent) {
_counter--;
}
_controller.add(CounterState(_counter));
}
void dispose() {
_controller.close();
_eventController.close();
_subscription.cancel();
}
}
更新插件的 pubspec.yaml
确保你的插件 pubspec.yaml
文件包含必要的依赖和元数据。
name: custom_bloc
description: A custom Flutter plugin for handling business logic.
version: 0.0.1
homepage:
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.20.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
plugin:
platforms:
android:
package: com.example.custom_bloc
pluginClass: CustomBlocPlugin
ios:
pluginClass: CustomBlocPlugin
注意:虽然这个示例没有实际的平台特定代码,但你需要为插件项目提供必要的平台和插件类定义。
2. 在Flutter应用中使用 custom_bloc
插件
添加依赖
在你的Flutter应用项目的 pubspec.yaml
文件中添加对 custom_bloc
插件的依赖。
dependencies:
flutter:
sdk: flutter
custom_bloc:
path: ../path/to/your/custom_bloc/plugin # 使用本地路径或发布到pub.dev后使用版本号
使用BLoC
在你的Flutter应用中,你可以这样使用 CounterBloc
。
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:custom_bloc/counter_bloc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterBloc counterBloc = CounterBloc();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Custom Bloc Example'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
StreamBuilder<CounterState>(
stream: counterBloc.state,
initialData: CounterState(0),
builder: (context, snapshot) {
return Text(
'${snapshot.data.count}',
style: Theme.of(context).textTheme.headline4,
);
},
),
],
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: () {
counterBloc.dispatch(IncrementEvent());
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
SizedBox(height: 10),
FloatingActionButton(
onPressed: () {
counterBloc.dispatch(DecrementEvent());
},
tooltip: 'Decrement',
child: Icon(Icons.remove),
),
],
),
),
);
}
@override
void dispose() {
// 在这里处理BLoC的dispose逻辑,通常在State类中使用
// counterBloc.dispose();
super.dispose();
}
}
注意:在上面的代码中,dispose
方法在 MyApp
类中被注释掉了。在真实的应用中,你应该在适当的 State
类中管理BLoC的生命周期,并在 dispose
方法中调用 counterBloc.dispose()
。
这个示例展示了如何创建和使用一个简单的自定义BLoC插件。根据你的业务需求,你可以扩展这个BLoC来处理更复杂的业务逻辑。