Flutter服务管理插件flutter_service_bloc的使用
Flutter服务管理插件flutter_service_bloc的使用
flutter_service_bloc
是一个用于在Flutter项目中实现服务层与Bloc架构集成的包。它可以帮助开发者更方便地管理服务请求、响应和错误处理。本文将详细介绍如何使用 flutter_service_bloc
,并提供一个完整的示例Demo。
1. 使用方法
1.1 Bloc
flutter_service_bloc
的核心是 ServiceBloc
,它继承自 Bloc
,专门用于处理服务层的逻辑。你可以通过 ServiceBloc
来发起服务请求,并根据请求的结果(成功、失败、加载中)来更新UI。关于 Bloc
的详细用法,请参考 service_bloc。
1.2 Listener
ServiceBlocListener
用于监听 ServiceBloc
的状态变化,并根据不同的状态执行相应的操作。常见的使用场景包括显示加载对话框、导航到其他页面或与其他 Bloc
进行通信。
- Dialog:当服务请求处于加载状态时显示加载对话框,请求成功或失败后关闭对话框。
ServiceBlocListener<ProductCheckoutServiceBloc, ProductCheckoutServiceRequested, String>(
onLoading: (context, state, event) => showLoading(),
onResponded: (context, state, event) => hideLoading(),
onFailed: (context, state, event, error) => showErrorDialog(context, error),
),
- Navigate:当服务请求成功后,导航到指定的页面。
ServiceBlocListener<ProductCheckoutServiceBloc, ProductCheckoutServiceRequested, String>(
onSucceed: (context, state, event, data) => Navigator.of(context).pushNamed('/your_route'),
),
- Bloc通信:当某个服务请求成功后,触发其他
Bloc
的事件。例如,用户登出后清除用户数据。
ServiceBlocListener<UserLogoutServiceBloc, UserLogoutServiceRequested, dynamic>(
onSucceed: (context, state, event, data) => context.read<UserCubit>().clearUserData(),
),
1.3 Builder
ServiceBlocBuilder
用于构建UI,它会根据 ServiceBloc
的状态返回不同的Widget。你可以为加载中、成功和失败分别定义不同的UI。
ServiceBlocBuilder<OpenLibraryAuthorDetailServiceBloc, OpenLibraryAuthorDetailServiceRequested, OpenLibraryAuthorDetail>(
onLoading: (context, state, event) => // your loading widget,
onSucceed: (context, state, event, data) {
return // your success widget
},
onFailed: (context, state, event, error) => // your failure widget,
),
2. 完整示例Demo
下面是一个完整的示例,展示了如何使用 flutter_service_bloc
来实现一个简单的书籍作者搜索和详情展示功能。这个示例包括了 ServiceBloc
的创建、ServiceBlocListener
和 ServiceBlocBuilder
的使用。
2.1 主应用入口
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_service_bloc/flutter_service_bloc.dart';
import 'open_library/repository/repository.dart';
void main() {
runApp(MultiRepositoryProvider(
providers: [
RepositoryProvider(
create: (context) => OpenLibraryRepository(),
)
],
child: const MyApp(),
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: const HomePage(),
onGenerateRoute: (settings) {
late final Widget page;
switch (settings.name) {
case HomePage.routeName:
page = const HomePage();
break;
case OpenLibraryAuthorSearchPage.routeName:
page = BlocProvider(
create: (context) => OpenLibraryAuthorSearchServiceBloc(
context.read<OpenLibraryRepository>()),
child: const OpenLibraryAuthorSearchPage(),
);
break;
case OpenLibraryAuthorDetailPage.routeName:
assert(settings.arguments is OpenLibraryAuthorDetailPageParameter);
final parameter =
settings.arguments as OpenLibraryAuthorDetailPageParameter;
page = BlocProvider(
create: (context) => OpenLibraryAuthorDetailServiceBloc(
context.read<OpenLibraryRepository>())
..add(OpenLibraryAuthorDetailServiceRequested(parameter.key)),
child: OpenLibraryAuthorDetailPage(parameter: parameter),
);
break;
default:
throw UnimplementedError('page name not found');
}
return MaterialPageRoute(
builder: (context) => page,
settings: settings,
);
},
);
}
}
2.2 主页
class HomePage extends StatelessWidget {
static const routeName = '/home';
const HomePage({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('flutter_service_bloc example'),
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ElevatedButton(
onPressed: () =>
Navigator.of(context).pushNamed(OpenLibraryAuthorSearchPage.routeName),
child: const Text('Open Library API'),
)
],
),
),
),
);
}
}
2.3 作者搜索页面
class OpenLibraryAuthorSearchPage extends StatefulWidget {
static const routeName = '/openLibraryAuthorSearch';
const OpenLibraryAuthorSearchPage({super.key});
[@override](/user/override)
State<OpenLibraryAuthorSearchPage> createState() =>
_OpenLibraryAuthorSearchPageState();
}
class _OpenLibraryAuthorSearchPageState extends State<OpenLibraryAuthorSearchPage> {
late final TextEditingController _textEditingController;
late final ScrollController _scrollController;
[@override](/user/override)
void initState() {
super.initState();
final serviceBloc = context.read<OpenLibraryAuthorSearchServiceBloc>();
_textEditingController = TextEditingController();
_textEditingController.addListener(() {
// 搜索框内容变化时重新加载数据
serviceBloc.add(OpenLibraryAuthorSearchReloadServiceRequested(_textEditingController.text));
});
_scrollController = ScrollController();
_scrollController.addListener(() {
// 当滚动到底部时加载更多数据
if (_scrollController.position.maxScrollExtent - _scrollController.position.pixels <= 144.0 &&
serviceBloc.pagination.hasNextPage) {
serviceBloc.add(OpenLibraryAuthorSearchServiceRequested(_textEditingController.text));
}
});
}
[@override](/user/override)
void dispose() {
_scrollController.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Author Search'),
actions: [
ServiceBlocBuilder<
OpenLibraryAuthorSearchServiceBloc,
OpenLibraryAuthorSearchServiceRequested,
List<OpenLibraryAuthorSearchResult>>(
onLoading: (context, state, event) => const Padding(
padding: EdgeInsets.all(8.0),
child: Center(
child: CircularProgressIndicator(
color: Colors.white,
),
),
),
onSuccess: (context, state, event, response) => const SizedBox(),
onFailure: (context, state, event, error) => const SizedBox(),
),
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
TextField(
controller: _textEditingController,
decoration: const InputDecoration(
hintText: 'Search author',
prefixIcon: Icon(Icons.search),
),
),
Expanded(
child: ServiceBlocBuilder<
OpenLibraryAuthorSearchServiceBloc,
OpenLibraryAuthorSearchServiceRequested,
List<OpenLibraryAuthorSearchResult>>(
onSuccess: (context, state, event, response) {
return ListView.builder(
controller: _scrollController,
itemBuilder: (context, index) {
final author = response[index];
return ListTile(
title: Text(author.name),
onTap: () => Navigator.of(context).pushNamed(
OpenLibraryAuthorDetailPage.routeName,
arguments: OpenLibraryAuthorDetailPageParameter(
key: author.key, name: author.name)),
);
},
itemCount: response.length,
);
},
),
),
],
),
),
);
}
}
2.4 作者详情页面
class OpenLibraryAuthorDetailPageParameter extends Equatable {
const OpenLibraryAuthorDetailPageParameter({
required this.key,
required this.name,
});
final String key;
final String name;
[@override](/user/override)
List<Object?> get props => [key, name];
}
class OpenLibraryAuthorDetailPage extends StatelessWidget {
static const routeName = '/openLibraryAuthorDetail';
const OpenLibraryAuthorDetailPage({super.key, required this.parameter});
final OpenLibraryAuthorDetailPageParameter parameter;
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(parameter.name)),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ServiceBlocBuilder<OpenLibraryAuthorDetailServiceBloc,
OpenLibraryAuthorDetailServiceRequested, OpenLibraryAuthorDetail>(
onLoading: (context, state, event) =>
const Center(child: CircularProgressIndicator()),
onSuccess: (context, state, event, response) {
return ListView(
children: [
if (response.photoIdList.isNotEmpty) ...[
SizedBox(
height: 320,
child: PageView.builder(
itemBuilder: (context, index) {
final photoId = response.photoIdList[index];
return Image.network(
'https://covers.openlibrary.org/a/id/$photoId.jpg',
errorBuilder: (context, error, stackTrace) =>
const Placeholder(color: Colors.red),
);
},
itemCount: response.photoIdList.length,
),
),
const Divider(),
],
Text('Name: ${response.name}'),
const Divider(),
if (response.personalName != null) ...[
Text('Personal Name: ${response.personalName}'),
const Divider(),
],
if (response.alternateNameList.isNotEmpty) ...[
Text(
'Alternate names: ${response.alternateNameList.take(10).join(', ')}'),
const Divider(),
],
if (response.title != null) ...[
Text('Title: ${response.title}'),
const Divider(),
],
if (response.birthDate != null) ...[
Text('Birthday: ${response.birthDate}'),
const Divider(),
],
if (response.wikipedia != null)
Text('Wikipedia: ${response.wikipedia}'),
],
);
},
),
),
);
}
}
更多关于Flutter服务管理插件flutter_service_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter服务管理插件flutter_service_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何使用 flutter_service_bloc
插件来管理 Flutter 应用中的服务的代码示例。flutter_service_bloc
是一个用于在 Flutter 应用中管理和协调后台服务的插件,它结合了 BLoC(Business Logic Component)设计模式来更好地组织代码。
首先,确保你已经在 pubspec.yaml
文件中添加了 flutter_service_bloc
依赖:
dependencies:
flutter:
sdk: flutter
flutter_service_bloc: ^最新版本号
然后运行 flutter pub get
来获取依赖。
接下来,我们创建一个简单的服务和一个 BLoC 来管理这个服务。
1. 创建服务类
首先,定义一个简单的服务类,比如一个计时服务:
import 'dart:async';
class TimerService {
Timer? _timer;
int _counter = 0;
StreamController<int> _controller = StreamController<int>();
Stream<int> get counterStream => _controller.stream;
void start() {
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
_counter++;
_controller.add(_counter);
});
}
void stop() {
_timer?.cancel();
_controller.close();
}
void dispose() {
_controller.close();
}
}
2. 创建 BLoC 类
接下来,创建一个 BLoC 类来管理 TimerService
:
import 'package:bloc/bloc.dart';
import 'package:flutter_service_bloc/flutter_service_bloc.dart';
import 'package:meta/meta.dart';
import 'timer_service.dart';
part 'timer_event.dart';
part 'timer_state.dart';
class TimerBloc extends Bloc<TimerEvent, TimerState> with ServiceBloc<TimerService> {
TimerBloc() : super(TimerInitial()) {
on<TimerStarted>((event, emit) async {
service = TimerService();
service!.start();
service!.counterStream.listen((counter) {
add(TimerTicked(counter));
});
emit(TimerRunning());
});
on<TimerStopped>((event, emit) async {
service?.stop();
service?.dispose();
emit(TimerStopped());
});
on<TimerTicked>((event, emit) {
emit(TimerRunning(counter: event.counter));
});
on<TimerReset>((_, emit) async {
service?.stop();
service?.dispose();
service = TimerService();
emit(TimerInitial());
});
@override
Future<void> close() {
service?.dispose();
return super.close();
}
}
@override
TimerService? createService() => TimerService();
}
3. 定义事件和状态类
在 timer_event.dart
文件中定义事件:
part of 'timer_bloc.dart';
abstract class TimerEvent {}
class TimerStarted extends TimerEvent {}
class TimerStopped extends TimerEvent {}
class TimerTicked extends TimerEvent {
final int counter;
TimerTicked(this.counter);
}
class TimerReset extends TimerEvent {}
在 timer_state.dart
文件中定义状态:
part of 'timer_bloc.dart';
abstract class TimerState {}
class TimerInitial extends TimerState {}
class TimerRunning extends TimerState {
final int? counter;
TimerRunning({this.counter});
}
class TimerStopped extends TimerState {}
4. 使用 BLoC 和服务
最后,在你的 Flutter 组件中使用 TimerBloc
:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:your_app/bloc/timer_bloc.dart';
void main() {
runApp(MultiBlocProvider(
providers: [
BlocProvider<TimerBloc>(
create: (context) => TimerBloc(),
),
],
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: TimerPage(),
);
}
}
class TimerPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Timer')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BlocBuilder<TimerBloc, TimerState>(
builder: (context, state) {
if (state is TimerRunning) {
return Text('Counter: ${state.counter ?? 0}');
} else if (state is TimerStopped || state is TimerInitial) {
return Text('Timer is stopped');
}
return Container();
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
context.read<TimerBloc>().add(TimerStarted());
},
child: Text('Start'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () {
context.read<TimerBloc>().add(TimerStopped());
},
child: Text('Stop'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () {
context.read<TimerBloc>().add(TimerReset());
},
child: Text('Reset'),
),
],
),
),
);
}
}
以上代码展示了如何使用 flutter_service_bloc
插件来管理一个简单的计时服务。你可以根据实际需求扩展服务和 BLoC 的功能。