refactor: replace provider with bloc in ViewSection

This commit is contained in:
appflowy 2022-04-26 16:28:56 +08:00
parent c8945133dc
commit 0b1f0ed401
5 changed files with 179 additions and 73 deletions

View File

@ -43,11 +43,19 @@ class AppBloc extends Bloc<AppEvent, AppState> {
appListener.start(
viewsChanged: (result) {
result.fold(
(views) => add(AppEvent.didReceiveViewUpdated(views)),
(views) {
if (!isClosed) {
add(AppEvent.didReceiveViewUpdated(views));
}
},
(error) => Log.error(error),
);
},
appUpdated: (app) => add(AppEvent.appDidUpdate(app)),
appUpdated: (app) {
if (!isClosed) {
add(AppEvent.appDidUpdate(app));
}
},
);
}
@ -149,40 +157,73 @@ class AppState with _$AppState {
);
}
class AppViewDataNotifier extends ChangeNotifier {
List<View> _views = [];
View? _selectedView;
class AppViewDataContext extends ChangeNotifier {
final String appId;
final ValueNotifier<List<View>> _viewsNotifier = ValueNotifier([]);
final ValueNotifier<View?> _selectedViewNotifier = ValueNotifier(null);
ExpandableController expandController = ExpandableController(initialExpanded: false);
AppViewDataNotifier() {
AppViewDataContext({required this.appId}) {
_setLatestView(getIt<MenuSharedState>().latestOpenView);
getIt<MenuSharedState>().addLatestViewListener((view) {
_setLatestView(view);
});
}
void _setLatestView(View? view) {
view?.freeze();
_selectedView = view;
_expandIfNeed();
VoidCallback addSelectedViewChangeListener(void Function(View?) callback) {
listener() {
callback(_selectedViewNotifier.value);
}
_selectedViewNotifier.addListener(listener);
return listener;
}
View? get selectedView => _selectedView;
void removeSelectedViewListener(VoidCallback listener) {
_selectedViewNotifier.removeListener(listener);
}
set views(List<View> views) {
if (_views != views) {
_views = views;
void _setLatestView(View? view) {
view?.freeze();
if (_selectedViewNotifier.value != view) {
_selectedViewNotifier.value = view;
_expandIfNeed();
notifyListeners();
}
}
View? get selectedView => _selectedViewNotifier.value;
set views(List<View> views) {
if (_viewsNotifier.value != views) {
_viewsNotifier.value = views;
_expandIfNeed();
notifyListeners();
}
}
UnmodifiableListView<View> get views => UnmodifiableListView(_viewsNotifier.value);
VoidCallback addViewsChangeListener(void Function(UnmodifiableListView<View>) callback) {
listener() {
callback(views);
}
_viewsNotifier.addListener(listener);
return listener;
}
void removeViewsListener(VoidCallback listener) {
_viewsNotifier.removeListener(listener);
}
void _expandIfNeed() {
if (_selectedView == null) {
if (_selectedViewNotifier.value == null) {
return;
}
if (!_views.contains(_selectedView!)) {
if (!_viewsNotifier.value.contains(_selectedViewNotifier.value)) {
return;
}
@ -193,6 +234,4 @@ class AppViewDataNotifier extends ChangeNotifier {
});
}
}
UnmodifiableListView<View> get views => UnmodifiableListView(_views);
}

View File

@ -0,0 +1,82 @@
import 'dart:async';
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part 'menu_view_section_bloc.freezed.dart';
class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
void Function()? _viewsListener;
void Function()? _selectedViewlistener;
final AppViewDataContext appViewData;
ViewSectionBloc({
required this.appViewData,
}) : super(ViewSectionState.initial(appViewData)) {
on<ViewSectionEvent>((event, emit) async {
await event.map(
initial: (e) async {
_startListening();
},
setSelectedView: (_SetSelectedView value) {
if (state.views.contains(value.view)) {
emit(state.copyWith(selectedView: value.view));
} else {
emit(state.copyWith(selectedView: null));
}
},
didReceiveViewUpdated: (_DidReceiveViewUpdated value) {
emit(state.copyWith(views: value.views));
},
);
});
}
void _startListening() {
_viewsListener = appViewData.addViewsChangeListener((views) {
if (!isClosed) {
add(ViewSectionEvent.didReceiveViewUpdated(views));
}
});
_selectedViewlistener = appViewData.addSelectedViewChangeListener((view) {
if (!isClosed) {
add(ViewSectionEvent.setSelectedView(view));
}
});
}
@override
Future<void> close() async {
if (_selectedViewlistener != null) {
appViewData.removeSelectedViewListener(_selectedViewlistener!);
}
if (_viewsListener != null) {
appViewData.removeViewsListener(_viewsListener!);
}
return super.close();
}
}
@freezed
class ViewSectionEvent with _$ViewSectionEvent {
const factory ViewSectionEvent.initial() = _Initial;
const factory ViewSectionEvent.setSelectedView(View? view) = _SetSelectedView;
const factory ViewSectionEvent.didReceiveViewUpdated(List<View> views) = _DidReceiveViewUpdated;
}
@freezed
class ViewSectionState with _$ViewSectionState {
const factory ViewSectionState({
required List<View> views,
View? selectedView,
}) = _ViewSectionState;
factory ViewSectionState.initial(AppViewDataContext appViewData) => ViewSectionState(
views: appViewData.views,
selectedView: appViewData.selectedView,
);
}

View File

@ -18,11 +18,11 @@ class MenuApp extends StatefulWidget {
}
class _MenuAppState extends State<MenuApp> {
late AppViewDataNotifier notifier;
late AppViewDataContext viewDataContext;
@override
void initState() {
notifier = AppViewDataNotifier();
viewDataContext = AppViewDataContext(appId: widget.app.id);
super.initState();
}
@ -46,16 +46,16 @@ class _MenuAppState extends State<MenuApp> {
),
BlocListener<AppBloc, AppState>(
listenWhen: (p, c) => p.views != c.views,
listener: (context, state) => notifier.views = state.views,
listener: (context, state) => viewDataContext.views = state.views,
),
],
child: BlocBuilder<AppBloc, AppState>(
builder: (context, state) {
return ChangeNotifierProvider.value(
value: notifier,
child: Consumer<AppViewDataNotifier>(
builder: (context, notifier, _) {
return expandableWrapper(context, notifier);
value: viewDataContext,
child: Consumer<AppViewDataContext>(
builder: (context, viewDataContext, _) {
return expandableWrapper(context, viewDataContext);
},
),
);
@ -65,9 +65,9 @@ class _MenuAppState extends State<MenuApp> {
);
}
ExpandableNotifier expandableWrapper(BuildContext context, AppViewDataNotifier notifier) {
ExpandableNotifier expandableWrapper(BuildContext context, AppViewDataContext viewDataContext) {
return ExpandableNotifier(
controller: notifier.expandController,
controller: viewDataContext.expandController,
child: ScrollOnExpand(
scrollOnExpand: false,
scrollOnCollapse: false,
@ -86,7 +86,7 @@ class _MenuAppState extends State<MenuApp> {
value: Provider.of<AppearanceSettingModel>(context, listen: true),
child: MenuAppHeader(widget.app),
),
expanded: _renderViewSection(notifier),
expanded: ViewSection(appViewData: viewDataContext),
collapsed: const SizedBox(),
),
],
@ -95,20 +95,9 @@ class _MenuAppState extends State<MenuApp> {
);
}
Widget _renderViewSection(AppViewDataNotifier notifier) {
return MultiProvider(
providers: [ChangeNotifierProvider.value(value: notifier)],
child: Consumer(
builder: (context, AppViewDataNotifier notifier, child) {
return ViewSection(appData: notifier);
},
),
);
}
@override
void dispose() {
notifier.dispose();
viewDataContext.dispose();
super.dispose();
}
}

View File

@ -3,35 +3,34 @@ import 'dart:developer';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
import 'package:app_flowy/workspace/application/menu/menu_view_section_bloc.dart';
import 'package:app_flowy/workspace/application/view/view_ext.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:reorderables/reorderables.dart';
import 'package:styled_widget/styled_widget.dart';
import 'item.dart';
class ViewSection extends StatelessWidget {
final AppViewDataNotifier appData;
const ViewSection({Key? key, required this.appData}) : super(key: key);
final AppViewDataContext appViewData;
const ViewSection({Key? key, required this.appViewData}) : super(key: key);
@override
Widget build(BuildContext context) {
// The ViewSectionNotifier will be updated after AppDataNotifier changed passed by parent widget
return ChangeNotifierProxyProvider<AppViewDataNotifier, ViewSectionNotifier>(
create: (_) {
return ViewSectionNotifier(
context: context,
views: appData.views,
initialSelectedView: appData.selectedView,
);
return BlocProvider(
create: (context) {
final bloc = ViewSectionBloc(appViewData: appViewData);
bloc.add(const ViewSectionEvent.initial());
return bloc;
},
update: (_, notifier, controller) => controller!..update(notifier),
child: Consumer(builder: (context, ViewSectionNotifier notifier, child) {
return _SectionItems(views: notifier.views);
}),
child: BlocBuilder<ViewSectionBloc, ViewSectionState>(
builder: (context, state) {
return _SectionItems(views: state.views);
},
),
);
}
@ -135,11 +134,12 @@ class _SectionItemsState extends State<_SectionItems> {
}
bool _isViewSelected(BuildContext context, String viewId) {
final view = context.read<ViewSectionNotifier>().selectedView;
if (view == null) {
return false;
}
return view.id == viewId;
// final view = context.read<ViewSectionNotifier>().selectedView;
// if (view == null) {
// return false;
// }
// return view.id == viewId;
return false;
}
}
@ -151,7 +151,6 @@ class ViewSectionNotifier with ChangeNotifier {
VoidCallback? _latestViewDidChangeFn;
ViewSectionNotifier({
required BuildContext context,
required List<View> views,
View? initialSelectedView,
}) : _views = views,
@ -192,7 +191,7 @@ class ViewSectionNotifier with ChangeNotifier {
View? get selectedView => _selectedView;
void update(AppViewDataNotifier notifier) {
void update(AppViewDataContext notifier) {
views = notifier.views;
}

View File

@ -13,7 +13,6 @@ import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/workspace.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:expandable/expandable.dart';
import 'package:flowy_infra/time/duration.dart';
@ -199,9 +198,7 @@ class MenuSharedState {
final ValueNotifier<View?> _latestOpenView = ValueNotifier<View?>(null);
MenuSharedState({View? view}) {
if (view != null) {
_latestOpenView.value = view;
}
_latestOpenView.value = view;
}
View? get latestOpenView => _latestOpenView.value;
@ -210,17 +207,17 @@ class MenuSharedState {
_latestOpenView.value = view;
}
VoidCallback addLatestViewListener(void Function(View?) latestViewDidChange) {
onChanged() {
latestViewDidChange(_latestOpenView.value);
VoidCallback addLatestViewListener(void Function(View?) callback) {
listener() {
callback(_latestOpenView.value);
}
_latestOpenView.addListener(onChanged);
return onChanged;
_latestOpenView.addListener(listener);
return listener;
}
void removeLatestViewListener(VoidCallback fn) {
_latestOpenView.removeListener(fn);
void removeLatestViewListener(VoidCallback listener) {
_latestOpenView.removeListener(listener);
}
}