Hi I have a Scaffold screen with a TabView and an AppBar.
I want change AppBar title when TabView index changed.
there is an AppFramebloc and there are to kind of events and states one for changing tabs and the others for AppBar title.
I used two BlocProvider for both the TabView and the AppBar separately to pass them AppFramebloc.
so everything works fine when app initialized for the first time. but when I navigate to a new screen and come back to the first screen the AppBar bloc works only and have interact with AppFramebloc and TabView bloc doesn't get any state.
here is the line that fails to work when come back from new screen (poping)
https://github.com/navidshad/foodbar_app/blob/master/lib/screens/screen_appFrame.dart#L43
here are widgets code:
main screen
class AppFrame extends StatefulWidget {
@override
_AppFrameState createState() => _AppFrameState();
}
class _AppFrameState extends State<AppFrame>
with SingleTickerProviderStateMixin {
AppFrameBloc bloc;
FrameTabType currentTab;
TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 2);
_tabController.addListener(onTabViewChanged);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
bloc = BlocProvider.of<AppFrameBloc>(context);
}
@override
Widget build(BuildContext context) {
print('build appframe');
return Scaffold(
appBar: CustomAppBar(),
body: BlocBuilder(
bloc: bloc,
condition: (old, state) {
return AppFrameBloc.blocCondition(state, [InitialAppFrameState, ShowingTabAppFrameState]);
},
builder: (stateContext, AppFrameState state) {
currentTab = state.tabType;
if (state is ShowingTabAppFrameState)
_tabController.index = getTypeIndex(state.tabType);
return TabBarView(
controller: _tabController,
physics: NeverScrollableScrollPhysics(),
children: <Widget>[
MenuTab(),
CartTab(),
],
);
},
),
);
}
int getTypeIndex(FrameTabType type) {
if (type == FrameTabType.MENU)
return 0;
else
return 1;
}
void onTabViewChanged() {
//FrameTabType type = AppFrameBloc.switchType(currentTab);
//sbloc.add(ChangeAppBarAppFrameEvent(type));
}
@override
void dispose() {
bloc.close();
super.dispose();
}
}
custom AppBar
class CustomAppBar extends StatefulWidget with PreferredSizeWidget {
@override
_CustomAppBarState createState() => _CustomAppBarState();
@override
Size get preferredSize => new Size.fromHeight(55);
}
class _CustomAppBarState extends State<CustomAppBar> {
FrameTabType currentTab;
AppFrameBloc bloc;
@override
void didChangeDependencies() {
super.didChangeDependencies();
bloc = BlocProvider.of<AppFrameBloc>(context);
}
@override
Widget build(BuildContext context) {
return BlocBuilder(
bloc: bloc,
condition: (old, state) {
return AppFrameBloc.blocCondition(
state, [InitialAppFrameState, ChangingAppBarAppFrameState]);
},
builder: (stateContext, AppFrameState state) {
return buildAppBar(state.title, state.tabType);
},
);
}
AppBar buildAppBar(String title, FrameTabType type) {
Widget actionBtn;
currentTab = type;
// setup title and action button according to tabType
if (type == FrameTabType.MENU) {
actionBtn = FlatButton(
child: Icon(FoodBarIcons.shopping_bag),
onPressed: onAppBarActionButtonPressed,
);
} else if (type == FrameTabType.CART) {
actionBtn = FlatButton(
child: Icon(FoodBarIcons.spoon_and_fork),
onPressed: onAppBarActionButtonPressed,
);
}
// build appbar
return AppBar(
title: Text(title),
actions: <Widget>[actionBtn],
elevation: 4,
);
}
void onAppBarActionButtonPressed() {
FrameTabType type = AppFrameBloc.switchType(currentTab);
bloc.add(ChangeTabAppFrameEvent(type));
}
@override
void dispose() {
bloc.close();
super.dispose();
}
}
@navidshad this is because you are closing the bloc when it is still being used. When providing a bloc via BlocProvider you do not need to close it manually as it is closed by BlocProvider automatically.
If you refactor your screen_appFrame.dart and all other screens like so:
import 'package:Food_Bar/screens/screens.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:Food_Bar/bloc/bloc.dart';
import 'package:Food_Bar/settings/settings.dart';
import 'package:Food_Bar/widgets/widgets.dart';
import 'package:Food_Bar/screens/tab_menu.dart';
class AppFrame extends StatefulWidget {
@override
_AppFrameState createState() => _AppFrameState();
}
class _AppFrameState extends State<AppFrame>
with SingleTickerProviderStateMixin {
FrameTabType currentTab;
TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 2);
_tabController.addListener(onTabViewChanged);
}
@override
Widget build(BuildContext context) {
print('build appframe');
return Scaffold(
appBar: CustomAppBar(),
body: BlocBuilder<AppFrameBloc, AppFrameState>(
condition: (old, state) {
return AppFrameBloc.blocCondition(
state, [InitialAppFrameState, ShowingTabAppFrameState]);
},
builder: (stateContext, AppFrameState state) {
currentTab = state.tabType;
if (state is ShowingTabAppFrameState)
_tabController.index = getTypeIndex(state.tabType);
return TabBarView(
controller: _tabController,
physics: NeverScrollableScrollPhysics(),
children: <Widget>[
MenuTab(),
CartTab(),
],
);
},
),
);
}
int getTypeIndex(FrameTabType type) {
if (type == FrameTabType.MENU)
return 0;
else
return 1;
}
void onTabViewChanged() {
//FrameTabType type = AppFrameBloc.switchType(currentTab);
//bloc.add(ChangeAppBarAppFrameEvent(type));
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
}
then everything should work because the bloc will not be closed prematurely.
Hope that helps! Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation 馃憤
Thanks for response, I was in a hurry and I wrote my own BLoC module. But I will use BLoC package in production.