I have DefaultTabController with 3 tabs. One of the tabs is a list. I need that when clicking on a list item, current list change on other widget. How do I do this? Thank you. Tab State Class
class StationState extends State<Stations> {
Widget secondWidget;
#override
Widget build(BuildContext context) {
secondWidget = Styles(this);
return DefaultTabController(
length: 3,
child: Scaffold(
backgroundColor: Color(0xFF000000),
appBar: AppBar(
title: HeaderLogo(),
backgroundColor: Color(0xFF000000),
bottom: TabBar(
indicatorColor: Colors.white,
tabs: [
Tab(
text: 'Favorites',
),
Tab(
text: 'Genres',
),
Tab(
text: 'Networks',
),
],
),
),
body: TabBarView(children: [
Favorites(),
secondWidget,
Networks(),
]),
),
);
;
}
}
Please add you code to help you better.
But you can pass a Callback to the list item, and when the list item is clicked, the callback is fired.
Something like this
return ListItem(
onPressed: () {
setState((){
//Do what you need on the parent
});
}
);
Related
screentshot
In my app there are some features in home page,what I want is when direct to their sub pages and still keep the buttom navigation bar.
Code for navigation bar Below the answer
Code for parts of home page
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold( resizeToAvoidBottomInset:false,
body: SlidingUpPanel(
body: Center(
child:Container(
constraints: BoxConstraints.expand(),
margin: const EdgeInsets.only(top:23),
child: Column(
children: [
.....
Container(
width: 730,
height: 190,
alignment:Alignment.center,
child:Wrap(
children: <Widget>[
//...otherFiveFeatures...//
OutlinedButton(
onPressed:()async{
var nav = await Navigator.of(context).pushNamed('/routerMobileScannerPage');
if(nav==true||nav==null)
{Navigator.of(context).pushNamedAndRemoveUntil('/routerHomePage',(Route<dynamic>route)=>false);
}
},
),
],
),
)
],
),
),
),
collapsed: Container(),
panel: Center(),
),
)
);
}
To achieve this, you need to manage multiple widgets for a single selection index. For example, from Home Screen you want to navigate to Details screen keeping the Home tab selected, you need to manage a flag for that selection. Something like this.
Code to get widget based on selection
Widget _getBodyWidget() {
switch (currentIndex) {
case 0:
return shouldShowDetails ? DetailsView() : HomeView();
case 1:
return CategoriesView();
default:
return HomeView();
}
}
In the code above, there is a flag shouldShowDetails which will be assigned as true when user taps on the Details button. When user wants to come to HomeScreen, change to false.
For such scenarios, I would suggest you to use the Provider plugin. It provides us an easy way to update the widget state based on such flags.
Code for buttom navigation bar
class PageCTRLWidget extends State<statePageCTRLWidget> with AutomaticKeepAliveClientMixin{
#override
bool get wantKeepAlive => true;
int currentIndex=0;
final screens=[
stateHomePageWidget(),
Center(child: Text('Categories',style: TextStyle(fontSize: 45),),),
Center(child: Text('Assistant',style: TextStyle(fontSize: 45),),),
stateMemberPageWidget()
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: currentIndex,
children: screens,
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.orange,
currentIndex: currentIndex,
onTap:(tappedIndex)=>setState(()=>currentIndex=tappedIndex),
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.menu_book_rounded),
label: 'Categories',
),
BottomNavigationBarItem(
icon: Icon(Icons.add_location_alt_rounded),
label: 'Assistant',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_box_rounded),
label: 'Member',
)
],
),
);
}
}
So in here i want to change some condition if my tabview switch to the second tab but i don't know how to get the tabbar index, already try this and that. Im hoping some solution without statefull, Im using GetX thanks.
im planning to change the extendBody: true, in my main page to false when the tab switch to the second tab i had the logic for that hopefully but the only problem is the index :(.
My tabs :
List<Tab> myTabs = [
Tab(
text: 'Following',
),
Tab(
text: 'Trending',
),
Tab(
text: 'Search',
),
];
DefaultController code :
DefaultTabController(
length: myTabs.length,
child: Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: bgColor,
// APPBAR
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
toolbarHeight: 60,
// BOTTOM
bottom: PreferredSize(
preferredSize: const Size.fromHeight(0),
child: Align(
alignment: Alignment.centerLeft,
child: TabBar(
isScrollable: true,
labelPadding: EdgeInsets.only(left: 20),
labelColor: Colors.white,
labelStyle: poppins.copyWith(
fontSize: 15,
fontWeight: bold,
),
unselectedLabelColor: Color(0xff585861),
indicatorColor: Colors.white.withOpacity(0),
indicatorSize: TabBarIndicatorSize.label,
// TABS
tabs: myTabs,
),
),
),
),
body: TabBarView(
children: [
FollowingTab(),
TrendingTab(),
search(),
],
),
),
);
Use TabBar with TabController and you can find current index while switching to next tab
#override
void initState() {
super.initState();
_controller = TabController(length: 6, vsync: this);
_controller!.addListener(() {
print(_controller!.index);
});
}
your build method be like:
#override
Widget build(BuildContext context) {
return Scaffold(
bottom:TabBar(
controller: _controller,
tabs:[
//your tabs will be here
]
),
body:TabBarView(
controller: _controller,
children: [
//your tabbarview will be here
]
),
);
}
Here i just code of Tabbar with using Getx and Stateless Widget.
CheckOut my code and if you find solution then give up me. Thanks in advance
class TabDemo extends StatelessWidget {
TabDemo({Key? key}) : super(key: key);
final DemoController demoController = Get.put(DemoController());
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: const TabBar(
tabs: [
Tab(icon: Icon(Icons.flight)),
Tab(icon: Icon(Icons.directions_transit)),
Tab(icon: Icon(Icons.directions_car)),
],
),
title: const Text('Tabs Demo'),
),
body: TabBarView(
controller: demoController.controller?.value,
children: const [
Icon(Icons.flight, size: 350),
Icon(Icons.directions_transit, size: 350),
Icon(Icons.directions_car, size: 350),
],
),
),
);
}
}
You need to create controller class to define controller and initmethod
class DemoController extends GetxController with SingleGetTickerProviderMixin {
Rx<TabController>? controller;
#override
void onInit() {
// TODO: implement onInit
controller?.value = TabController(length: 6, vsync: this);
controller?.value.addListener(() {
print(controller?.value.index);
});
super.onInit();
}
}
I have a menu page which has tabA and tabB. The appBar has a search button.
menu.dart
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Item List'),
actions: [
_currentIndex == 0
? Row(
children: [
IconButton(
onPressed: () {
Navigator.pushNamed(context, SearchScreen.ROUTE,
arguments: list);
},
icon: Icon(
Icons.search,
color: Colors.white,
)),
IconButton(
icon: Icon(
Icons.tune,
color: Colors.white,
),
onPressed: () {
...
}
}
});
},
)
],
)
: Container()
],
bottom: TabBar(
unselectedLabelColor: Colors.white,
labelColor: Colors.white,
isScrollable: false,
tabs: <Widget>[
Tab(
text: "Tab A",
),
Tab(
text: "Tab B",
),
],
controller: _tabController,
indicatorColor: Colors.white,
indicatorSize: TabBarIndicatorSize.tab,
),
bottomOpacity: 1,
),
body: TabBarView(
controller: _tabController,
children: <Widget>[
ShowTabA(),
ShowTabB()
],
),
);
}
}
In tabA, it has a listView. When I tap on one of the listview item, it will navigate to EditPage. When return from EditPage, I will refresh the data in tabA. All the list item will be updated.
tabA.page
initState(){
_bloc.getItemList();
}
Navigator.pushNamed(EditPage.ROUTE,context).then((onValue){
_bloc.getItemList();
});
My problem here:
When I click the search icon in menu page , it will navigate to SearchScreen page.Once user click the list item in SearchScreen, it will navigate to EditPage.
When back from EditPage, I want the list in TabA page refreshed but it can't because I navigate using pushReplacementNamed in menu page . How to achieve that ?
SearchScreen.dart
Navigator.pushReplacementNamed(context);
Updated answer on Modified Question:
Since you are using pushReplacement your await won't work. You can use the following approach to make it work:
First you have to define your routes:
routes: <String, WidgetBuilder> {
'/': (BuildContext context) => // Your MainPage,
'/menu': (BuildContext context) => MenuPage(),
'/search': (BuildContext context) => SearchPage(),
'/edit': (BuildContext context) => EditPage(),
}
Navigation from Menu to Search:
void goToSearch() async {
await Navigator.pushNamed(context, "/search", arguments: list);
_bloc.getItemList().then((onValue){
setState(() {
list = onValue;
});
});
}
Use pushNamed instead of pushReplacementNamed when navigating from Search to Edit:
Navigator.of(context).pushNamed("/edit");
Important step When you have to go back to Menu from Edit, you've to use popUntil method:
Navigator.popUntil(context, ModalRoute.withName('/menu'));
I followed the tutorial ( https://flutter.dev/docs/cookbook/design/tabs ) on how to create a flutter tabbar, if I wanted the user to add / remove tabs, how can I do it dynamically ?
class TabBarDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
Tab(icon: Icon(Icons.directions_bike)),
],
),
title: Text('Tabs Demo'),
),
body: TabBarView(
children: [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
],
),
),
),
);
}
}
When I create a tab the scroll must point to the new generated item
When I delete a tab the scroll must point to the previous element
First of all you need to convert your Stateless widget to a stateful widget. Then set the icons as a variable list.
class TabBarDemoState extends State<TabBarDemo> {
var icons = List<Icon>();
initState() {
super.initState();
icons = [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
];
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: icons,
),
title: Text('Tabs Demo'),
),
body: TabBarView(
children: icons,
),
),
),
);
}
}
Now say you want to remove an icon from the list. Write a method callback for it, for example a button press. Call this method when the button press or when an event occurs. Something like this:
class TabBarDemoState extends State<TabBarDemo> {
var icons = List<Icon>();
initState() {
super.initState();
icons = [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
];
}
void removeAnIcon() {
if (icons.isNotEmpty) {
setState({
icons.removeAt(0);
});
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: icons,
),
title: InkWell(
onTap: () => removeAnIcon(),
Text('Tabs Demo')
),
),
body: TabBarView(
children: icons,
),
),
),
);
}
}
When you call setState then the build function is called again and widget tree is rebuilt. Therefore, when you press on the title then you will remove one icon and get one less icon. Read more about Stateful widgets here
I am using sliver list and I want to use both floating action button and sliding_up_panel from pub with the following behaviour: when I scroll down my list, the floating action button disappears; when I scroll up the fab appears. Moreover, the fab should disappear when I slide up (open) the menu.
As you can see above, the floating action button is all on the sliding element, but I want it to be between the sliding element and the scrolling item list.
Also in above picture, the problem is that the floating button is actually visible but I want to hide it with a nice animation when I slide up the sliding menu.
I hope my question is clear!!!
Edit please do it with scroll controller
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SliverExample(
bodyWidgets: Text('Hello Body'),
backgroundWidget: Text('Hello Background'),
),
);
}
}
Widget listItem(Color color, String title) => Container(
height: 100.0,
color: color,
child: Center(
child: Text(
"$title",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 14.0,
fontWeight: FontWeight.bold),
),
),
);
class SliverExample extends StatefulWidget {
final Widget backgroundWidget;
final Widget bodyWidgets;
SliverExample({
this.backgroundWidget,
this.bodyWidgets,
});
#override
_SliverExampleState createState() => _SliverExampleState();
}
class _SliverExampleState extends State<SliverExample> {
// I need something like this
// To determine if SliverAppBar is expanded or not.
ScrollController _scrollController;
bool isAppBarExpanded = false;
#override
void initState() {
super.initState();
_scrollController = ScrollController()
..addListener(() => setState(() {
print('Scroll view Listener is called offset ${_scrollController.offset}');
}));
}
bool get _changecolor {
return _scrollController.hasClients
&& _scrollController.offset > (200-kToolbarHeight);
}
bool get _hideFAB {
return _scrollController.hasClients
&& _scrollController.offset > (200-kToolbarHeight);
}
#override
Widget build(BuildContext context) {
// To change the item's color accordingly
// To be used in multiple places in code
//Color itemColor = isAppBarExpanded ? Colors.white : Colors.black;
// In my case PrimaryColor is white,
// and the background widget is dark
return Scaffold(
body: CustomScrollView(
controller: _scrollController,
slivers: <Widget>[
SliverAppBar(
pinned: true,
leading: BackButton(
color: _changecolor? Colors.white: Colors.black, // Here
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.shopping_cart,
color: _changecolor? Colors.white: Colors.black, // Here
),
onPressed: () {},
),
],
expandedHeight: 200.0,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
'title',
style: TextStyle(
fontSize: 18.0,
color: _changecolor? Colors.white: Colors.black, // Here
),
),
// Not affecting the question.
background: widget.backgroundWidget,
),
),
SliverList(
///Use SliverChildListDelegate and provide a list
///of widgets if the count is limited
///
///Lazy building of list
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
/// To convert this infinite list to a list with "n" no of items,
/// uncomment the following line:
/// if (index > n) return null;
return listItem(Colors.grey, "Sliver List item: $index");
},
/// Set childCount to limit no.of items
/// childCount: 100,
),
),
// Not affecting the question.
SliverToBoxAdapter(child: widget.bodyWidgets),
],
),
floatingActionButton: _hideFAB? Container() : FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add,),
),
);
}
}