我认为的BLoC模式
---------分割线20210927
通过底部组件的滑动,来变更饼图的状态
/// Splits a stream of consecutive strings into lines.
///
/// The input string is provided in smaller chunks through
/// the `source` stream.
Stream lines(Stream 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.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 timedCounter(Duration interval, [int? maxCount]) async* {
int i = 0;
while (true) {
await Future.delayed(interval);
yield i++;
if (i == maxCount) break;
}
}
异步生成器方法返回 Stream 对象
Stream streamFromFutures(Iterable> futures) async* {
for (var future in futures) {
var result = await future;
yield result;
}
}
async*方法一般较少使用,在多数据源的处理上过于简单,所以引入了StreamController。
StreamSubscription> listen(
void Function(List 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 ctrl = StreamController.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();
StreamSink get configSink => _dataSourceController.sink;
Stream get configStream => _dataSourceController.stream;
///点击事件分发
final _eventController = StreamController();
StreamSink get dispatch => _eventController.sink;
/// UI内容展示
final _displayController = StreamController();
Stream 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 extends StatefulWidget {
final Widget child;
final T bloc;
const BlocProvider({Key? key, required this.bloc, required this.child})
: super(key: key);
static T? of(BuildContext context) {
// final type = _providerType>();
final BlocProvider? provider = context.findAncestorWidgetOfExactType>();
return provider?.bloc;
}
static Type _providerType() => T;
@override
State createState() => _BlocProviderState();
}
class _BlocProviderState extends State {
@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 listen(void onData(T event),
{Function onError, void onDone(), bool cancelOnError})
@override
Widget build(BuildContext context) {
final MainBloc? bloc = BlocProvider.of(context);
resetController(widget.config);
return Scaffold(
appBar: AppBar(
title: StreamBuilder(
stream: bloc?.displayContent,
builder: (BuildContext context, AsyncSnapshot 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 {
MainBloc() : super(MainInitial());
@override
Stream mapEventToState(
MainEvent event,
) async* {
// TODO: implement mapEventToState
}
}
///...
class MainBloc extends Bloc {
MainBloc() : super(MainState(selectedIndex: 0, isExtended: false));
@override
Stream 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
///...
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(builder: (context, state) {
return Text(
"选择Index:" + state.selectedIndex.toString(),
style: TextStyle(fontSize: 30.0),
);
}),
))
]);
}
Widget _buildBg({List 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(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(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(
builder: (context, state) {
return FloatingActionButton(
onPressed: () {
///添加NavigationRail展开,收缩事件
BlocProvider.of(context).add(IsExtendEvent());
},
child: Icon(state.isExtended ? Icons.send : Icons.navigation),
);
},
),
);
}
}
API:
BlocWidgets 类比于 StreamBuilder
BlocBuilder(
bloc: blocA, // provide the local bloc instance
builder: (context, state) {
// return widget here based on BlocA's state
}
)
BlocBuilder(
buildWhen: (previousState, state) {
/// return 是否重新运行
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
BlocProvider
BlocProvider.value(
value: BlocProvider.of(context),
child: ScreenA(),
);
/// 使用
{
// with extensions
context.read();
// without extensions
BlocProvider.of(context);
}
MultiBlocProvider
/// Rrovider 嵌套问题
MultiBlocProvider(
providers: [
BlocProvider(
create: (BuildContext context) => BlocA(),
),
BlocProvider(
create: (BuildContext context) => BlocB(),
),
BlocProvider(
create: (BuildContext context) => BlocC(),
),
],
child: ChildA(),
)
BlocListener
listener方法没用return值,用于 展示SnackBar,Dialog
BlocListener(
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container(),
)
BlocConsumer
融合了BlocListener 和BlocBuilder
BlocConsumer(
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 {
TestBloc(int i) : super(i);
@override
Stream 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的