flutter how to keep widget alive when open other widget - android

I use three TabBarView in my project with wantKeepAlive in every TabBarView, but the second tabBarView will dispose when i jump view from one to three。However when I switch to other widget which like fragment in Android , all the TabBarView will dispose,I cant't find anyone have the same problem just like me.Thanks First.
and this is my code
body: new TabBarView(controller: _tabController, children: <Widget>[
new EventsView(
presenter: _presenter,
key: Key("EventsView"),
),
new HistoryView(
presenter: _presenter,
key: Key("HistoryView"),
),
new NewsView(
key: Key("NewsView"),
presenter: _presenter,
),
]),
and if i select the drawer item
Widget build(BuildContext context) {
return Scaffold(
body: Builder(builder: (BuildContext context) {
this.scaffoldContext = context;
return _getFromIndex(position);
}));
}
_getFromIndex(int i) {
if (i == 0) {
return _dashboard();
} else if (i == 1) {
return new TeamView(key: Key("TeamView"),);
} else if (i == 2) {
return AllBookView();
} else if (i == 3) {
return AllDocView();
}
}

Related

Navigate to other tab in bottom navigation bar from HomeView?

I have a bottom navigation bar like this:
When i click (1) in tab Home, it navigate to Detail page of item in tab My Learning (2) but not tab My Learning (using Navigator.of(context).push()). But not change tab to My Learning in bottom navigation bar, it still tab Home. So how i fix that. Thank you.
Some code:
Class main_view_model (contain bottom navigation bar):
BottomTabItemAfterSignIn _currentBottomTab = BottomTabItemAfterSignIn.home;
BottomTabItemAfterSignIn get currentBottomTab => _currentBottomTab;
void changeTab(BottomTabItemAfterSignIn tab, {bool isBackClick = false}) {
if (_currentBottomTab != tab) {
_currentBottomTab = tab;
}
}
Class main_view:
#override
Widget buildView(BuildContext context) {
return WillPopScope(
onWillPop: () async {
_onPressBackDevice();
return false;
},
child: Selector<MainUserViewModel, BottomTabItemAfterSignIn>(
selector: (_, viewModel) => viewModel.currentBottomTab,
builder: (_, currentBottomTab, __) {
return Scaffold(
backgroundColor: AppColor.neutrals.shade900,
body: _buildBody(),
bottomNavigationBar: BottomNavigationUserWidget(
currentTab: currentBottomTab,
onSelectTab: _selectTab,
),
);
},
),
);
}
Widget _buildBody() {
if (viewModel.currentBottomTab == BottomTabItemAfterSignIn.home) {
return _buildTabItem(
BottomTabItemAfterSignIn.home,
_cacheBottomTabWidgets[BottomTabItemAfterSignIn.home],
);
} else if (viewModel.currentBottomTab == BottomTabItemAfterSignIn.course) {
return _buildTabItem(
BottomTabItemAfterSignIn.course,
_cacheBottomTabWidgets[BottomTabItemAfterSignIn.course],
);
} else if (viewModel.currentBottomTab ==
BottomTabItemAfterSignIn.myLearning) {
return _buildTabItem(
BottomTabItemAfterSignIn.myLearning,
_cacheBottomTabWidgets[BottomTabItemAfterSignIn.myLearning],
);
} else {
return _buildTabItem(
BottomTabItemAfterSignIn.setting,
_cacheBottomTabWidgets[BottomTabItemBeforeSignIn.setting],
);
}
}
Widget _buildTabItem(BottomTabItemAfterSignIn tabItem, Widget? child) {
// Cache Widget
return Offstage(
offstage: viewModel.currentBottomTab != tabItem,
child: child ??
(viewModel.currentBottomTab == tabItem
? _buildCacheTab(tabItem)
: Container()),
);
}
Widget _buildCacheTab(BottomTabItemAfterSignIn tabItem) {
return _cacheBottomTabWidgets[tabItem] =
BottomBodyNavigationUserWidget(
bottomMenuBar: tabItem,
navigatorKey: _bottomTabKeys[tabItem]!,
);
}
void _selectTab(BottomTabItemAfterSignIn bottomMenuBar) {
viewModel.changeTab(bottomMenuBar);
}
I hope you are using stateful widget if so then you have to use setState((){}) to to effect any value change so your code for _currentBottomTab = tab; should like this:
setState((){
_currentBottomTab = tab;
});
For more info on stateful widget please check here.
use the same fun on 1-tab nd pass the index number where you want to go
changeTab(2),

The argument type SearchBar can't be assigned to the parameter type Widget in flutter

I'm trying to implement an automatic Search Bar in Flutter.
When compiling I get the error: The argument type 'SearchBar' can't be assigned to the parameter type 'Widget?'.
class Home extends StatelessWidget {
Future<List<Post>> search(String search) async {
await Future.delayed(Duration(seconds: 2));
return List.generate(search.length, (int index) {
return Post(
"Title : $search $index",
"Description :$search $index",
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: SearchBar<Post>(
onSearch: search,
onItemFound: (Post post, int index) {
return ListTile(
title: Text(post.title),
subtitle: Text(post.description),
);
},
),
),
),
);
}
}
From the official Documentation, you need to call the build method to get a widget.
However, it would be better if you create your SearchBar inside your constructor itself, so that a new one doesn't get created every time.
From the official documentation,
class _MyHomePageState extends State<MyHomePage> {
SearchBar searchBar;
AppBar buildAppBar(BuildContext context) {
return new AppBar(
title: new Text('My Home Page'),
actions: [searchBar.getSearchAction(context)]
);
}
_MyHomePageState() {
searchBar = new SearchBar(
inBar: false,
setState: setState,
onSubmitted: print,
buildDefaultAppBar: buildAppBar
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: searchBar.build(context)
);
}
}

Know which Route was removed in onPopPage on Navigation 2.0 in Flutter

I want to know which Page was being removed by the navigation in Flutter (I'm using Navigation 2.0).
I have the following code:
#override
Widget build(BuildContext context) {
return Navigator(
key: this.navigatorKey,
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
// TODO check for the page that was poped
viewModel.otherUserIDForChat = null;
return true;
},
pages: _generatePages(),
);
}
As seen in the previous code I'm setting to null the otherUserIDForChat, however, I would like to know if the page that was poped was the chat screen that I've implemented inside the _generatePages, here's the code for that:
/// Function that aggregates pages depending on [AppViewModel]'s values
List<Page> _generatePages() {
List<Page> pages = [];
pages.add(
MaterialPage(
child: HomeScreen(),
),
);
if (viewModel.otherUserIDForChat != null) {
pages.add(
MaterialPage(
key: ValueKey(viewModel.otherUserIDForChat),
child: SingleChatScreen(
parameters: SingleChatScreenParameters(
otherUserID: viewModel.otherUserIDForChat,
),
),
),
);
}
return pages;
}
How can I know which page was being poped?
You can give your page a name:
MaterialPage(
key: ValueKey(viewModel.otherUserIDForChat),
name: viewModel.otherUserIDForChat,
child: SingleChatScreen( ... ),
),
then in opPopPage you can check for the name:
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
if (route.settings.name == viewModel.otherUserIDForChat) {
// do your thing
}
return true;
}

how to disable ui build when setchanged method in flutter

I have a graph view my app, whenever my function works it's also rebuilding
how to control my UI
var response = await getdashboarddata(tokenkey);
setState(() {
});
use FutureBuilder
Widget projectWidget() {
return FutureBuilder(
builder: (context, projectSnap) {
if (projectSnap.connectionState == ConnectionState.none &&
projectSnap.hasData == null) {
//print('project snapshot data is: ${projectSnap.data}');
return Container();
}
return ListView.builder(
itemCount: projectSnap.data.length,
itemBuilder: (context, index) {
ProjectModel project = projectSnap.data[index];
return Column(
children: <Widget>[
// Widget to display the list of project
],
);
},
);
},
future: getdashboarddata(tokenkey),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ProjectList'),
),
body: projectWidget(),
);
}

Replace widgets like fragments in Flutter

I'm new to Flutter.
I have an app with 2 sub widgets (2 fragments in Android), and when i clicked next button in WidgetA, I want to replace (or push) that widget into WidgetChildA, like push (or replace) fragments in Android. But instead of that, I got a fullscreen widget like a normal screen in Flutter.
Here is my code:
import 'package:flutter/material.dart';
class DemoFragment extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new DemoFragmentState();
}
}
class DemoFragmentState extends State<DemoFragment> {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Scaffold(
appBar: new AppBar(title: new Text("Demo fragment")),
body: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new FragmentA(),
new FragmentB()
],
),
);
}
}
class FragmentA extends StatelessWidget {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Center(
child: new Column(
children: <Widget>[
new Text("Fragment A"),
new RaisedButton(
child: new Text("next"),
onPressed: () {
print(context.toString() + context.hashCode.toString());
Navigator.of(context).push(new PageRouteBuilder(
opaque: true,
transitionDuration: const Duration(milliseconds: 0),
pageBuilder: (BuildContext context, _, __) {
return new FragmentChildA();
}));
/*showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text("Hello world"),
content: new Text("this is my content"),
));*/
})
],
),
);
}
}
class FragmentB extends StatelessWidget {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Center(
child: new Column(
children: <Widget>[
new Text("Fragment B"),
new RaisedButton(
child: new Text("next"),
onPressed: () {
print(context.toString() + context.hashCode.toString());
Navigator.of(context).push(new PageRouteBuilder(
opaque: true,
transitionDuration: const Duration(milliseconds: 0),
pageBuilder: (BuildContext context, _, __) {
return new FragmentChildB();
}));
})
],
));
}
}
class FragmentChildA extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[new Text("Fragment Child A")],
)));
}
}
class FragmentChildB extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[new Text("Fragment Child B")],
)));
}
}
Screenshots:
Home page
After clicked
I'm not sure if you can use the router to replace just the part of a view; but you could conditionally change which Widget you render in the build method, like this:
children: <Widget>[
someCondition ? new FragmentA() : new FragmentChildA(),
new FragmentB()
],
Then you just need to set someCondition by using setState in the stateful widget:
setState(() => someCondition = true);
If you want to do this from inside FragmentA you could allow it to have the function passed into its constructor:
new FragmentA(
onPress: setState(() => someCondition = true)
)
However, it might be better to encapsulate all of this logic inside a single widget so this logic isn't all hanging around in the parent. You could make a single StatefulWidget for FragementA which keeps track of which stage you're on, and then in its build method renders the correct child widget, something like:
build() {
switch(stage) {
Stages.Stage1:
return new Stage1(
onNext: () => setState(() => stage = Stages.Stage2);
);
Stages.Stage2:
return new Stage1(
onPrevious: () => setState(() => stage = Stages.Stage1);
);
}
}
You could simply use a MaterialApp widget with the CupertinoPageTransitionsBuilder as pageTransitionTheme like
MaterialApp(
theme: ThemeData(
pageTransitionsTheme: PageTransitionsTheme(builders: {
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.android: SlideRightPageTransitionsBuilder(),
}),
initialRoute: "fragment1",
routes: <String, WidgetBuilder>{
"fragment1": (BuildContext context) => Fragment1(),
"fragment2": (BuildContext context) => Fragment2(),
}
...
),
Then in fragment 1 you simply use the following to navigate to the other fragment with a slide animation
Navigator.of(context).pushReplacementNamed("fragment2");
Well, I found out the way to handle this case for a few months, but I just forgot to answer this question.
The solution is wrapping your Widget with a new Navigator.
You can see the video example here
And the simple demo for it here
The downside of this solution is sometimes, the keyboard is not showing as my intention.
ok I'm going to be doing this the same way google does it with the bottom navigation bar, I don't see this as the most performant but it works
class MainFabContainer extends StatefulWidget {
MainFabContainer({
Key key,
}) : super(key: key);
#override
State<StatefulWidget> createState() {
return MainFabContainerState();
}
}
class MainFabContainerState extends State<MainFabContainer> {
String title = "Title";
int _currentIndex = 0;
final List<int> _backstack = [0];
#override
Widget build(BuildContext context) {
//each fragment is just a widget which we pass the navigate function
List<Widget> _fragments =[Fragment1(navigate: navigateTo),Fragment2(navigate: navigateTo,),Fragment3(navigate: navigateTo,)];
//will pop scope catches the back button presses
return WillPopScope(
onWillPop: () {
customPop(context);
},
child: Scaffold(
drawer: drawer(),
appBar: AppBar(
title: Text(title),
),
body: Column(
children: <Widget>[
Expanded(
child: _fragments[_currentIndex],
),
],
),
),
);
}
void navigateTo(int index) {
_backstack.add(index);
setState(() {
_currentIndex = index;
});
}
void navigateBack(int index) {
setState(() {
_currentIndex = index;
});
}
customPop(BuildContext context) {
if (_backstack.length - 1 > 0) {
navigateBack(_backstack[_backstack.length - 1]);
} else {
_backstack.removeAt(_backstack.length - 1);
Navigator.pop(context);
}
}
//this method could be called by the navigate and navigate back methods
_setTitle(String appBarTitle) {
setState(() {
title = appBarTitle;
});
}
}

Categories

Resources