我认为的BLoC模式
---------分割线20210927
通过底部组件的滑动,来变更饼图的状态
/// Splits a stream of consecutive strings into lines.
///
/// The input string is provided in smaller chunks through
/// the `source` stream.
Stream<String> lines(Stream<String> source) async* {
// Stores any partial line from the previous chunk.
var partial = '';
// Wait until a new chunk is available, then process it.
await for (var chunk in source) {
var lines = chunk.split('\n');
lines[0] = partial + lines[0]; // Prepend partial line.
partial = lines.removeLast(); // Remove new partial line.
for (var line in lines) {
yield line; // Add lines to output stream.
}
}
// Add final partial line to output stream, if any.
if (partial.isNotEmpty) yield partial;
}
还有Stream支持的方法:
var counterStream =
Stream<int>.periodic(Duration(seconds: 1), (x) => x).take(15);
///...
counterStream.forEach(print); // Print an integer every second, 15 times.
var doubleCounterStream = counterStream.map((int x) => x * 2);
doubleCounterStream.forEach(print);
///...
.where((int x) => x.isEven) // Retain only even integer events.
.expand((var x) => [x, x]) // Duplicate each event.
.take(5) // Stop after the first five events.
asynchronous generator (async*) function 异步生成器函数
Stream<int> timedCounter(Duration interval, [int? maxCount]) async* {
int i = 0;
while (true) {
await Future.delayed(interval);
yield i++;
if (i == maxCount) break;
}
}
异步生成器方法返回 Stream 对象
Stream<T> streamFromFutures<T>(Iterable<Future<T>> futures) async* {
for (var future in futures) {
var result = await future;
yield result;
}
}
async*方法一般较少使用,在多数据源的处理上过于简单,所以引入了StreamController。
StreamSubscription<List<int>> listen(
void Function(List<int> event)? onData, {
Function? onError,
void Function()? onDone,
bool? cancelOnError,
}) {
if (_sub == null) {
throw StateError('Stdin has already been terminated.');
}
// ignore: close_sinks
final controller = _getCurrent();
if (controller.hasListener) {
throw StateError(''
'Subscriber already listening. The existing subscriber must cancel '
'before another may be added.');
}
///...
import 'dart:async';
void main() {
//
// Initialize a "Single-Subscription" Stream controller
//
final StreamController ctrl = StreamController();
//
// Initialize a single listener which simply prints the data
// as soon as it receives it
//
final StreamSubscription subscription = ctrl.stream.listen((data) => print('$data'));
//
// We here add the data that will flow inside the stream
//
ctrl.sink.add('my name');
ctrl.sink.add(1234);
ctrl.sink.add({'a': 'element A', 'b': 'element B'});
ctrl.sink.add(123.45);
//
// We release the StreamController
//
ctrl.close();
}
可以任意添加 listener 到 Broadcast Stream.
import 'dart:async';
void main() {
//
// Initialize a "Broadcast" Stream controller of integers
//
final StreamController<int> ctrl = StreamController<int>.broadcast();
//
// Initialize a single listener which filters out the odd numbers and
// only prints the even numbers
//
final StreamSubscription subscription = ctrl.stream
.where((value) => (value % 2 == 0))
.listen((value) => print('$value'));
//
// We here add the data that will flow inside the stream
//
for(int i=1; i<11; i++){
ctrl.sink.add(i);
}
//
// We release the StreamController
//
ctrl.close();
}
Dart | RXDart |
---|---|
Stream | StreamController |
Observable | Subject |
PublishSubject 和 广播的StreamController变体,只是返回的是Observable而不是Stream。
BehaviorSubject和PublishSubject 不同点: subscribe(订阅)时,返回订阅前 最新的event。
ReplaySubject和PublishSubject 不同点: subscribe(订阅)时,返回订阅前 所有的event。
在不需要时,一定要释放资源
///网络请求处理
final _dataSourceController = StreamController<ModelConfig>();
StreamSink<ModelConfig> get configSink => _dataSourceController.sink;
Stream<ModelConfig> get configStream => _dataSourceController.stream;
///点击事件分发
final _eventController = StreamController<UserEvent>();
StreamSink<UserEvent> get dispatch => _eventController.sink;
/// UI内容展示
final _displayController = StreamController<String>();
Stream<String> get displayContent => _displayController.stream;
enum UserEvent {
add, delete,modify
}
initBloc() {
eventController.listen((event) {
Switch (event) {
case UserEvent.add:
handleUserAdd();
break;
///...
});
}
void handleUserAdd() {
_displayController.sink.add('User click add button');
}
ConfigRepository.instance.get((config) {
configSink.add(config);
});
import 'package:flutter/cupertino.dart';
import 'package:flutter_app/bloc/bloc.dart';
class BlocProvider<T extends Bloc> extends StatefulWidget {
final Widget child;
final T bloc;
const BlocProvider({Key? key, required this.bloc, required this.child})
: super(key: key);
static T? of<T extends Bloc>(BuildContext context) {
// final type = _providerType<BlocProvider<T>>();
final BlocProvider<T>? provider = context.findAncestorWidgetOfExactType<BlocProvider<T>>();
return provider?.bloc;
}
static Type _providerType<T>() => T;
@override
State<StatefulWidget> createState() => _BlocProviderState();
}
class _BlocProviderState extends State<BlocProvider> {
@override
Widget build(BuildContext context) => widget.child;
@override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
}
创建BLoC,并通过BlocProvider 提供子Widget获取
void main() {
runApp(BlocProvider(
bloc: MainBloc(),
child: MaterialApp(
title: 'MaoShan',
home: RoutingPage()
),
));
}
监听Stream
StreamSubscription<T> listen(void onData(T event),
{Function onError, void onDone(), bool cancelOnError})
@override
Widget build(BuildContext context) {
final MainBloc? bloc = BlocProvider.of<MainBloc>(context);
resetController(widget.config);
return Scaffold(
appBar: AppBar(
title: StreamBuilder<String>(
stream: bloc?.displayContent,
builder: (BuildContext context, AsyncSnapshot<String?> snapshot) {
return Text(snapshot?.data ?? '');
}),
),
body: Container(
child: ElevatedButton(
child: Text("Add something"),
onPressed: () => bloc?.dispatch.add)));
}
BLoC内部架构:
flutter_bloc: ^7.0.1
bloc内容
mapEventToState将Event转换为State
class MainBloc extends Bloc<MainEvent, MainState> {
MainBloc() : super(MainInitial());
@override
Stream<MainState> mapEventToState(
MainEvent event,
) async* {
// TODO: implement mapEventToState
}
}
///...
class MainBloc extends Bloc<MainEvent, MainState> {
MainBloc() : super(MainState(selectedIndex: 0, isExtended: false));
@override
Stream<MainState> mapEventToState(MainEvent event) async* {
///main_view中添加的事件,会在此处回调,此处处理完数据,将数据yield,BlocBuilder就会刷新组件
if (event is SwitchTabEvent) {
///获取到event事件传递过来的值,咱们拿到这值塞进MainState中
///直接在state上改变内部的值,然后yield,只能触发一次BlocBuilder,它内部会比较上次MainState对象,如果相同,就不build
yield MainState()
..selectedIndex = event.selectedIndex
..isExtended = state.isExtended;
} else if (event is IsExtendEvent) {
yield MainState()
..selectedIndex = state.selectedIndex
..isExtended = !state.isExtended;
}
}
}
main_event:这里是执行的各类事件,有点类似fish_redux的action层
@immutable
abstract class MainEvent {}
///...
@immutable
abstract class MainEvent extends Equatable{
const MainEvent();
}
///切换NavigationRail的tab
class SwitchTabEvent extends MainEvent{
final int selectedIndex;
const SwitchTabEvent({@required this.selectedIndex});
@override
List<Object> get props => [selectedIndex];
}
///展开NavigationRail,这个逻辑比较简单,就不用传参数了
class IsExtendEvent extends MainEvent{
const IsExtendEvent();
@override
List<Object> get props => [];
}
main_state:状态数据放在这里保存,中转
@immutable
abstract class MainState {}
///...
class MainState{
int selectedIndex;
bool isExtended;
MainState({this.selectedIndex, this.isExtended});
}
Ui页面编写:发送event、更改UI
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _buildBg(children: [
//侧边栏
_buildLeftNavigation(),
//右边主体内容
Expanded(child: Center(
child: BlocBuilder<MainBloc, MainState>(builder: (context, state) {
return Text(
"选择Index:" + state.selectedIndex.toString(),
style: TextStyle(fontSize: 30.0),
);
}),
))
]);
}
Widget _buildBg({List<Widget> children}) {
///创建BlocProvider的,表明该Page,我们是用MainBloc,MainBloc是属于该页面的Bloc了
return BlocProvider(
create: (BuildContext context) => MainBloc(),
child: Scaffold(
appBar: AppBar(title: Text('Bloc')),
body: Row(children: children),
),
);
}
//增加NavigationRail组件为侧边栏
Widget _buildLeftNavigation() {
return BlocBuilder<MainBloc, MainState>(builder: (context, state) {
return NavigationRail(
backgroundColor: Colors.white,
elevation: 3,
extended: state.isExtended,
labelType: state.isExtended
? NavigationRailLabelType.none
: NavigationRailLabelType.selected,
//侧边栏中的item
destinations: [
NavigationRailDestination(
icon: Icon(Icons.add_to_queue),
selectedIcon: Icon(Icons.add_to_photos),
label: Text("测试一"),
),
NavigationRailDestination(
icon: Icon(Icons.add_circle_outline),
selectedIcon: Icon(Icons.add_circle),
label: Text("测试二"),
),
NavigationRailDestination(
icon: Icon(Icons.bubble_chart),
selectedIcon: Icon(Icons.broken_image),
label: Text("测试三"),
),
],
//顶部widget
leading: _buildNavigationTop(),
//底部widget
trailing: _buildNavigationBottom(),
selectedIndex: state.selectedIndex,
onDestinationSelected: (int index) {
///添加切换tab事件
BlocProvider.of<MainBloc>(context)
.add(SwitchTabEvent(selectedIndex: index));
},
);
});
}
Widget _buildNavigationTop() {
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3383029432,2292503864&fm=26&gp=0.jpg",
),
fit: BoxFit.fill,
),
),
),
),
);
}
Widget _buildNavigationBottom() {
return Container(
child: BlocBuilder<MainBloc, MainState>(
builder: (context, state) {
return FloatingActionButton(
onPressed: () {
///添加NavigationRail展开,收缩事件
BlocProvider.of<MainBloc>(context).add(IsExtendEvent());
},
child: Icon(state.isExtended ? Icons.send : Icons.navigation),
);
},
),
);
}
}
API:
BlocWidgets 类比于 StreamBuilder
BlocBuilder<BlocA, BlocAState>(
bloc: blocA, // provide the local bloc instance
builder: (context, state) {
// return widget here based on BlocA's state
}
)
BlocBuilder<BlocA, BlocAState>(
buildWhen: (previousState, state) {
/// return 是否重新运行
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
BlocProvider
BlocProvider.value(
value: BlocProvider.of<BlocA>(context),
child: ScreenA(),
);
/// 使用
{
// with extensions
context.read<BlocA>();
// without extensions
BlocProvider.of<BlocA>(context);
}
MultiBlocProvider
/// Rrovider 嵌套问题
MultiBlocProvider(
providers: [
BlocProvider<BlocA>(
create: (BuildContext context) => BlocA(),
),
BlocProvider<BlocB>(
create: (BuildContext context) => BlocB(),
),
BlocProvider<BlocC>(
create: (BuildContext context) => BlocC(),
),
],
child: ChildA(),
)
BlocListener
listener方法没用return值,用于 展示SnackBar,Dialog
BlocListener<BlocA, BlocAState>(
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container(),
)
BlocConsumer
融合了BlocListener 和BlocBuilder
BlocConsumer<BlocA, BlocAState>(
listenWhen: (previous, current) {
// return true/false to determine whether or not
// to invoke listener with state
},
listener: (context, state) {
// do stuff here based on BlocA's state
},
buildWhen: (previous, current) {
// return true/false to determine whether or not
// to rebuild the widget with state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
Bloc
enum TestEvent { increment }
class TestBloc extends Bloc<TestEvent, int> {
TestBloc(int i) : super(i);
@override
Stream<int> mapEventToState(TestEvent event) async* {
switch (event) {
case TestEvent.increment:
yield state + 1;
break;
}
}
}
ChangeNotifier 被观察者
ChangeNotifierProvider 观察者、Widget
Key key
@required T value
Widget child
/// ChangeNotifierProvider(/// create: (_) => new MyChangeNotifier(),/// child: .../// )
性能好点(具体看注解)
之后再填吧,我是写在了内部网站,这是copy的