Open pishguy opened 5 years ago
@MahdiPishguy I found that several backs should be unique with no duplicate for example F1 Dashboard F2 Settings F1 Dashboard Then I should have only 1 back to F2 Settings but no more backs. The stack should be a Set"Map"... Right? Later we could add 2 ways "Replace" and "Push" We should have a base fragment like dashboard then a nested Childs like "Dashboard>> "Profile" >> "Edit Profile" "Settings" "About" I will think more about the right UX how could we define a structured routes I will keep this issue open until I figure out a solid solution
@amorenew
in profile you have another screen like with fragment? ok, so how can we manage back stack? this is very difficult to handle, may be we have more nested fragment into fragment, for example:
profile -> show profile -> update profile ..............-> edit profile -> update profile -> show profile
i think you can review instagram application
@MahdiPishguy I think in show profile if it is the base then it should clear edit profile -> update profile because in Show profile user still can access edit button The issue is UX will confuse the user who expects go back he will go back several times to minimize the app. Chrome or web behavior is not suitable for mobile apps Still, we can have the 2 cases it is not a big issue just a boolean flag
@amorenew yes this is not big issue, but i think is this very good for us to implementing correct way, i have another question? how to set each fragment as base? for example by default dashborad is base, how to make profile as base like with that?
@MahdiPishguy I didn't figure out that I need R&D Currently I made a Map similar to Flutter routes
FragmentManager().setRoutes(<String, Widget>{
FRAGMENT_DASHBOARD: const DashboardPage(),
FRAGMENT_DRIVER_CHECKIN: const DriverCheckInPage(),
FRAGMENT_DRIVER_CHECKIN_CONFIRM: const DriverCheckInConfirmPage(),
FRAGMENT_MESSAGES: const MessagesPage(),
});
My fragment manager
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:supervisor_flutter/widgets/fragment_bloc/fragment.dart';
class FragmentManager {
factory FragmentManager() {
return _singleton;
}
FragmentManager._internal();
static final FragmentManager _singleton = FragmentManager._internal();
int _currentIndex = 0;
final List<int> _backstack = [0];
Map<String, Widget> _fragmentsMap = <String, Widget>{};
List<Widget> _fragments = [];
void setRoutes(Map<String, Widget> fragmentsMap) {
_fragmentsMap = fragmentsMap;
}
Map<String, Widget> getRoutes() {
return _fragmentsMap;
}
void addFragment(String routeName) {
_fragments.add(getRoutes()[routeName]);
}
void navigateTo(int index) {
_backstack.add(index);
}
bool isExist(String fragmentName) {
bool exist = false;
_fragments.asMap().forEach((int index, Widget value) {
if (value is Fragment && value.getRouteName() == fragmentName) {
exist = true;
} else if (value is BlocProvider &&
value.child is Fragment &&
(value.child as Fragment).getRouteName() == fragmentName) {
exist = true;
} else if (value is MultiBlocProvider &&
value.child is Fragment &&
(value.child as Fragment).getRouteName() == fragmentName) {
exist = true;
}
});
return exist;
}
int navigateToName(String fragmentName) {
int fragmentIndex = -1;
print('manager navigateToName:$fragmentIndex');
_fragments.asMap().forEach((int index, Widget value) {
if (value is Fragment && value.getRouteName() == fragmentName) {
fragmentIndex = index;
} else if (value is BlocProvider &&
value.child is Fragment &&
(value.child as Fragment).getRouteName() == fragmentName) {
fragmentIndex = index;
} else if (value is MultiBlocProvider &&
value.child is Fragment &&
(value.child as Fragment).getRouteName() == fragmentName) {
fragmentIndex = index;
}
});
print('manager navigateToName:$fragmentIndex');
_backstack.add(fragmentIndex);
_currentIndex = fragmentIndex;
return fragmentIndex;
}
int navigateBack() {
return --_currentIndex;
}
Future<bool> backPop() {
if (_currentIndex > 0) {
return Future<bool>.value(false);
} else {
return Future<bool>.value(true);
}
}
void setFragments(List<Widget> fragments) {
_fragments = fragments;
}
Widget getCurrentFragment(int index) {
if (_fragments.isEmpty) addFragment(getRoutes().entries.first.key);
return _fragments[index];
}
}
I added extra features like AppBar stay same and it gets the title of the current fragment So AppBar and Drawer not changed.
MainAppBar(
title: currentFragment.getTitle(),
)
I will merge when I test more the different cases
i have a problem now, i can't use this implementation on body
of Scaffold
BlocProvider.of() called with a context that does not contain a Bloc of type FragmentBloc
change fragments on body with clicking on bottom sheet buttons
class Home extends StatefulWidget{
@override
State<StatefulWidget> createState()=> _HomeState();
}
class _HomeState extends State<Home> with TickerProviderStateMixin{
AnimationController _draggableBottomSheetController;
Duration _duration = Duration(milliseconds: 500);
BottomBarController controller;
@override
void initState() {
super.initState();
controller = BottomBarController(vsync: this, dragLength: 550, snap: true);
_draggableBottomSheetController = AnimationController(vsync: this, duration: _duration);
}
@override
Widget build(BuildContext context) {
return BlocProvider(
builder: (BuildContext context)=>FragmentBloc(),
child: Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
appBar: ApplicationToolbar(
title: Fa.appName,
),
body: Dashboard(),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: GestureDetector(
child: FloatingActionButton(
child: AnimatedIcon(icon: AnimatedIcons.menu_close, progress: _draggableBottomSheetController),
elevation: 5,
backgroundColor: Colors.deepOrange,
foregroundColor: Colors.white,
onPressed: () => controller.swap(),
),
),
bottomNavigationBar: BottomExpandableAppBar(
expandedHeight: 350,
horizontalMargin: 3,
bottomOffset: 10.0,
controller: controller,
attachSide: Side.Bottom,
shape: AutomaticNotchedShape(RoundedRectangleBorder(), StadiumBorder(side: BorderSide())),
expandedBackColor: Theme.of(context).backgroundColor,
expandedBody: Center(
child: Stack(children: <Widget>[
Text("Hello world!"),
]),
),
bottomAppBarBody: Container(
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
SizedBox(width: 20.0),
RaisedButton(
color: Colors.white,
child: Text(
'مقالات سایت',
style: Theme.of(context).textTheme.caption.copyWith(fontFamily: 'IranSansLight', color: Colors.black),
),
onPressed:()=>BlocProvider.of<FragmentBloc>(context).dispatch(FragmentNavigateEvent(PageScreenPosts)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0))),
SizedBox(width: 50.0),
RaisedButton(
color: Colors.white,
child: Text(
'لیست دوره',
style: Theme.of(context).textTheme.caption.copyWith(fontFamily: 'IranSansLight', color: Colors.black),
),
onPressed:()=>BlocProvider.of<FragmentBloc>(context).dispatch(FragmentNavigateEvent(PageScreenSections)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0))),
SizedBox(width: 20.0),
],
),
),
),
),
),
);
}
}
class Dashboard extends StatelessWidget {
@override
Widget build(BuildContext context) {
FragmentManager()
..setFragments([ScreenPosts(),ScreenSections()]);
return WillPopScope(
onWillPop: () {
BlocProvider.of<FragmentBloc>(context).dispatch(FragmentBackEvent());
return FragmentManager().backPop();
},
child: BlocBuilder<FragmentBloc, FragmentState>(
builder: (BuildContext context, FragmentState state) {
if (state is FragmentCurrentState)
return FragmentManager().getCurrentFragment(state.index);
else if (state is FragmentBackState)
return FragmentManager().getCurrentFragment(state.index);
else
return FragmentManager().getCurrentFragment(0);
}),
);
}
}
@MahdiPishguy I am not sure it looks fine to me If you created a sample repo I may help
http://uplod.ir/m2puehoav73t/kelide_jazzb.7z.htm
click on green button
@MahdiPishguy I found the issue You should use dispatch on widget under bloc builder So you need to wrap the scaffold by
child: BlocBuilder<FragmentBloc, FragmentState>(
builder: (BuildContext context, FragmentState state) {
Not the Dashboard
@amorenew I fixed thanks
@MahdiPishguy follow here if you need to add a drawer logic https://github.com/felangel/bloc/issues/407
@amorenew there is one issue as using flag to have more fragment in backStack
@MahdiPishguy I will do it when I have time
you suppose i click multiple on
navigate to fragment X
for example 20 click on that,_fragment1 _fragment2 _fragment3 _fragment4 _fragment1 _fragment2 _fragment3 _fragment4 _fragment1 _fragment2 ...
now when i press on back button i exit application from history on two click, history should be back done.
i think problem is
_currentIndex