i have some problems when i coded basic layout namely, I found that when I choose a card two indexes away, a notification is displayed in the console, which in each of the tabs in the function at the start of a given widget, I wrote, instead of refreshing one tab, the selected one, it refreshes the neighboring one and finally the target one. I will post photos with the code below and let me know what's wrong! :)
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_HomeScreen createState() => _HomeScreen();
}
class _HomeScreen extends State<HomePage> with TickerProviderStateMixin {
TabController controllerInMain;
#override
void initState() {
super.initState();
controllerInMain = TabController(length: 5, vsync: this, initialIndex: 2);
controllerInMain.addListener(() {
_handleTabSelection();
});
}
void _handleTabSelection() {
setState(() {});
}
#override
void dispose() {
controllerInMain.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Managly",
style: TextStyle(color: Colors.black),
),
),
resizeToAvoidBottomPadding: false,
body: SafeArea(
child: TabBarView(
children: <Widget>[
MenuTab(),
OrdersTab(),
StatisticsTab(),
EmployeeTab(),
TableTab(),
],
controller: controllerInMain,
),
),
bottomNavigationBar: TabBar(
controller: controllerInMain,
indicatorColor: Theme.of(context).primaryColor,
tabs: <Tab>[
Tab(
icon: Icon(
Icons.restaurant_menu,
color: Colors.amber,
),
),
Tab(
icon: Icon(
Icons.receipt_long,
color: Colors.amber,
),
),
Tab(
icon: Icon(
Icons.insights,
color: Colors.amber,
),
),
Tab(
icon: Icon(
Icons.group,
color: Colors.amber,
),
),
Tab(
icon: Icon(
Icons.weekend,
color: Colors.amber,
),
),
],
),
);
}
}
Panel UI
Console output
Related
I am making Flutter app i which I want to design bottom sheet with TabBar view as below design.
I have seen option for bottom sheet in Scafold() but onlz tabs are visible. I dont know how to animate it as the above.
My code so far is as below
class MainScreen extends StatefulWidget {
static final String id = 'MainScreen';
const MainScreen({Key? key}) : super(key: key);
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateMixin {
TabController? _tabController;
bool isNull = false;
#override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this, initialIndex: 2);
_tabController!.addListener(_tabChanged);
}
void _tabChanged() {
if (_tabController!.indexIsChanging) {
print('tabChanged: ${_tabController!.index}');
}
}
#override
void dispose() {
_tabController!.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: TabBarView(
controller: _tabController,
children: [
HomePage(),
PersonalData(),
LoginPage(),
RegistrationPage()
],
)
),
bottomSheet: SafeArea(
child: TabBar(
controller: _tabController,
labelColor: color,
indicatorColor: Colors.black,
unselectedLabelColor: Colors.black,
tabs: [
const Tab(
icon: Icon(
Icons.home_outlined,
//color: Colors.white,
),
),
const Tab(
icon: Icon(
Icons.shopping_cart_outlined,
//color: Colors.white,
),
),
const Tab(
icon: Icon(
Icons.favorite_border,
//color: Colors.white,
),
),
const Tab(
icon: Icon(
Icons.send,
//color: Colors.white,
),
)
],
)
),
);
}
}
AnyOne has some idea how to do it?? If there is some package to use for this bottom sheet behaviour ??
I am using footer menu to navigate som page aside the main home page which already has AppBar with a title, I want it to be overridden.
I want it, if I navigate from Home page to meditation page, I want the menu to overridden to "MEDITATION".
Here's my footer menu:
class Footer extends StatefulWidget {
// Footer({Key? key}) : super(key: key);
#override
_FooterState createState() => _FooterState();
}
class _FooterState extends State<Footer> {
var _selectedIndex = 0;
final pages = [
Dashboard(),
Health(),
Meditation(),
Sleep(),
Sounds(),
];
#override
void initState() {
super.initState();
_selectedIndex = 0;
}
#override
Widget build(BuildContext context) {
// return GestureDetector(
return Scaffold(
bottomNavigationBar: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: dbShadowColor,
offset: Offset.fromDirection(3, 1),
spreadRadius: 1,
blurRadius: 5)
]),
child: Db5BottomNavigationBar(
items: <Db5BottomNavigationBarItem>[
Db5BottomNavigationBarItem(icon: db5_ic_home),
Db5BottomNavigationBarItem(icon: db5_ic_heart),
Db5BottomNavigationBarItem(icon: db5_ic_meditate),
Db5BottomNavigationBarItem(icon: db5_ic_sleep),
Db5BottomNavigationBarItem(icon: db5_ic_sounds),
],
currentIndex: _selectedIndex,
unselectedIconTheme: IconThemeData(color: db5_icon_color, size: 24),
selectedIconTheme: IconThemeData(color: db5_colorPrimary, size: 24),
onTap: (int index) {
setState(() {
_selectedIndex = index;
});
},
type: Db5BottomNavigationBarType.fixed,
),
),
body: SafeArea(
child: pages[_selectedIndex],
),
);
// );
}
}
Here's my Meditation Page code:
class Meditation extends StatefulWidget {
Meditation({Key? key}) : super(key: key);
#override
_MeditationState createState() => _MeditationState();
}
class _MeditationState extends State<Meditation> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: appStore.scaffoldBackground,
centerTitle: true,
title: Text(
"Meditate",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 20,
),
)),
body: Container(
alignment: Alignment.topLeft,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(),
child: text(
meditation_text_title,
),
)
],
),
),
),
);
}
}
This is how I rendered my footer on the main HOME PAGE:
return Scaffold(
appBar: AppBar(
backgroundColor: db5_white,
iconTheme: IconThemeData(color: sh_textColorPrimary),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
],
title: text(title,
textColor: sh_colorPrimary,
fontFamily: fontBold,
fontSize: textSizeNormal),
),
body: Stack(
children: <Widget>[
Dashboard(),
Footer(),
],
),
);
Am I doing it wrongly?
Here's how you should implement your screens.
// Name the class whatever you like
class ScreensController extends StatefulWidget {
const ScreensController({Key? key}) : super(key: key);
#override
_ScreensControllerState createState() => _ScreensControllerState();
}
class _ScreensControllerState extends State<ScreensController> {
int _selectedIndex = 0;
List<String> mainScreensTitles = [
'Dashboard',
'Health',
'Meditation',
'Sleep',
'Sounds'
];
List<Widget> screens= [
Dashboard(),
Health(),
Meditation(),
Sleep(),
Sounds(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
// Indexed stack is used to prevent page rebuilds when navigating between the
//screens
body: IndexedStack(
index: _selectedIndex,
children: screens,
),
bottomNavigationBar: BottomNavigationBar(
//Toggle these booleans if needed
showSelectedLabels: false,
showUnselectedLabels: false,
onTap: (newScreenIndex) {
setState(() {
_selectedIndex = newScreenIndex;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: mainScreensTitles[0],
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite_outline),
label: mainScreensTitles[1],
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: mainScreensTitles[2],
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: mainScreensTitles[3],
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: mainScreensTitles[4],
),
],
currentIndex: _selectedIndex,
),
);
}
}
Implementation of some of your screens:
Dashboard & Meditation:
class Dashboard extends StatelessWidget {
const Dashboard({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dashboard'),
),
body: Center(child: Text('Dashboard coming soon')),
);
}
}
class Meditation extends StatelessWidget {
const Meditation({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Meditation'),
),
body: Center(child: Text('Meditation coming soon')),
);
}
}
I'm new in flutter and I'm trying to make a TabBar view like in the figure. But it shows "No TabController for TabBar" and When creating a TabBarView, you must either provide an explicit TabController using the "controller" property, or you must ensure that there is a DefaultTabController above the TabBarView.
In this case, there was neither an explicit controller nor a default controller.
I have tried a suggestion that mentioned in the internet but still got error. Can anyone help me how to fix it?? Thank you
class _TransactionState extends State<Transaction> with SingleTickerProviderStateMixin {
int _value = 1;
TabController controller;
#override
void initState() {
controller = new TabController(vsync: this, length: 2);
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Transaction'),
actions: <Widget>[
DropdownButton(
dropdownColor: primary,
value: _value,
items: [
DropdownMenuItem(
child: Text(
"Daily",
style: TextStyle(color: Colors.white,
fontWeight: FontWeight.bold,
),
),
value: 1,
),
DropdownMenuItem(
child: Text("Monthly",
style: TextStyle(color: Colors.white,
fontWeight: FontWeight.bold,
),),
value: 2,
),
DropdownMenuItem(
child: Text("Yearly",
style: TextStyle(color: Colors.white,
fontWeight: FontWeight.bold,
),),
value: 3,
),
],
onChanged: (int value){
setState(() {
_value = value;
});
//Padding(padding: EdgeInsets.all(20.0));
style: new TextStyle(
color: Colors.white,
);
},
),
],
automaticallyImplyLeading: false,
backgroundColor: secondary,
bottom: new TabBar(
controller: controller,
tabs: <Widget>[
new Tab(icon: new Icon(Icons.add_shopping_cart),text: "Expense",),
new Tab(icon: new Icon(Icons.attach_money),text: "Income",),
],
),
),
body: TabBarView(
controller: controller,
children: <Widget>[
new expense.TransactionExpense(),
new income.TransactionIncome(),
],
),
);
}
}
This should solve it.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyAppState()
);
}
}
class MyAppState extends StatefulWidget{
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyAppState> with TickerProviderStateMixin {
TabController _controller;
final List<Tab> topTabs = <Tab>[
new Tab(text: 'Profile'),
new Tab(text: 'Match'),
new Tab(text: 'Chat'),
];
#override
void initState() {
super.initState();
_controller = TabController(vsync: this, length: 3);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MyApp'),
bottom: TabBar(
controller: _controller,
tabs: topTabs,
),
),
body: TabBarView(
controller: _controller,
children: [
new Container(
color: Colors.lightBlueAccent,
child: Center(child: Text('Profile', style: TextStyle(color: Colors.white),),),
),
new Container(
color: Colors.purpleAccent,
child: Center(child: Text('Match', style: TextStyle(color: Colors.white),),),
),
new Container(
color: Colors.lightGreenAccent,
child: Center(child: Text('Chat', style: TextStyle(color: Colors.white),),),
)
]),
);
}
}
I am using the method of creating multiple stacks with the bottom navigation bar outline at the article here.
It all works well but as there are a few techniques I'm not aware of in the method I'm struggling to find a way to navigate in my app.
I'm just trying to create a screen for profile which has a button that takes you back to feed. As there are some fancy things done in the tab_navigator I'm not sure how to do this. Can anyone help?
The tab navigator code is below.
import 'package:flutter/material.dart';
import 'package:highline_app/bottom_navigation.dart';
import 'package:highline_app/color_detail_page.dart';
import 'package:highline_app/colors_list_page.dart';
import 'package:highline_app/pages/feed.dart';
class TabNavigatorRoutes {
static const String root = '/';
static const String detail = '/detail';
static const String feed = '/feed';
static const String profile = '/profile';
}
class TabNavigator extends StatelessWidget {
TabNavigator({this.navigatorKey, this.tabItem});
final GlobalKey<NavigatorState> navigatorKey;
final TabItem tabItem;
void _push(BuildContext context, {int materialIndex: 500}) {
var routeBuilders = _routeBuilders(context, materialIndex: materialIndex);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => routeBuilders[TabNavigatorRoutes.detail](context),
),
);
}
Map<String, WidgetBuilder> _routeBuilders(BuildContext context,
{int materialIndex: 500}) {
return {
TabNavigatorRoutes.feed: (context) => NewsFeed(),
TabNavigatorRoutes.root: (context) => ColorsListPage(
color: activeTabColor[tabItem],
title: tabName[tabItem],
onPush: (materialIndex) =>
_push(context, materialIndex: materialIndex),
),
TabNavigatorRoutes.detail: (context) => ColorDetailPage(
color: activeTabColor[tabItem],
title: tabName[tabItem],
materialIndex: materialIndex,
),
};
}
#override
Widget build(BuildContext context) {
final routeBuilders = _routeBuilders(context);
return Navigator(
key: navigatorKey,
initialRoute: TabNavigatorRoutes.root,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(
builder: (context) => routeBuilders[routeSettings.name](context),
);
},
);
}
}
Actually, you don't need to use Navigator. I advise you keep it simple. You can do this with TabController. You can check following code to navigate between Pages or Tabs whatever you need.
import 'package:flutter/material.dart';
void main() => runApp(TabLayoutDemo());
class TabLayoutDemo extends StatefulWidget {
#override
_TabLayoutDemoState createState() => _TabLayoutDemoState();
}
class _TabLayoutDemoState extends State<TabLayoutDemo>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 4);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.yellow,
home: DefaultTabController(
length: 4,
child: Scaffold(
body: TabBarView(
controller: _tabController,
children: [
Container(
color: Colors.yellow,
),
Container(
color: Colors.orange,
),
// Feed Page.
Container(
color: Colors.lightGreen,
),
// Profile Page.
Container(
color: Colors.red,
child: Padding(
padding: EdgeInsets.only(top: 15.0),
child: SizedBox(
width: double.infinity,
child: RaisedButton.icon(
icon: Icon(Icons.arrow_back),
textColor: Colors.white,
color: Colors.lightBlue,
label: Text('Go To Feed Tab'),
onPressed: () {
setState(() {
_tabController.index = 2;
});
},
)),
),
),
],
),
bottomNavigationBar: TabBar(
controller: _tabController,
tabs: [
Tab(
icon: Icon(Icons.home),
),
Tab(
icon: Icon(Icons.settings),
),
// Here is feed tab button.
Tab(
icon: Icon(Icons.rss_feed),
),
// Here is profile tab button.
Tab(
icon: Icon(Icons.perm_identity),
),
],
labelColor: Colors.yellow,
unselectedLabelColor: Colors.blue,
indicatorSize: TabBarIndicatorSize.label,
indicatorPadding: EdgeInsets.all(5.0),
indicatorColor: Colors.red,
),
backgroundColor: Colors.black,
),
),
);
}
}
I implemented a basic TabBar and TabBarView with a DefaultTabController, see code below.
class MyApp2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: BOTTOM_TABS,
child: Scaffold(
appBar: AppBar(title: const Text('Bottom App Bar')),
body: _tabBarView(),
bottomNavigationBar: _bottomTabBar(),
),
);
}
_tabBarView() {
return TabBarView(
physics: NeverScrollableScrollPhysics(),
children: [
Container(
color: Colors.blue,
),
Container(
color: Colors.orange,
),
Container(
color: Colors.lightGreen,
),
Container(
color: Colors.red,
),
],
);
}
_bottomTabBar() {
return TabBar(
tabs: [
Tab(
icon: new Icon(Icons.home),
),
Tab(
icon: new Icon(Icons.public),
),
Tab(
icon: new Icon(Icons.group),
),
Tab(
icon: new Icon(Icons.person),
)
],
);
}
}
Works great! Now what I want to do is change the animation between the two tabs from the default animation. But I can't find an easy way to do that.
After a bit of research it seems like I need to use a custom TabController and somehow use its animateTo method. To me that seems like a pretty big change just to change the animation. What I wonder is if that is the correct way or if I am missing some easier way to just change the default animation between the tabviews?
If someone could give me some good resources to point me in the right direction I'd greatly appreciate it.
This is not hard, just use TabController (to do so you need to use SingleTickerProviderStateMixin ) and AnimatedBuilder.
class MyApp2 extends StatefulWidget {
#override
_MyApp2State createState() => _MyApp2State();
}
class _MyApp2State extends State<MyApp2> with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
_tabController = TabController(length: 4, vsync: this);
super.initState();
}
_tabBarView() {
return AnimatedBuilder(
animation: _tabController.animation,
builder: (BuildContext context, snapshot) {
return Transform.rotate(
angle: _tabController.animation.value * pi,
child: [
Container(
color: Colors.blue,
),
Container(
color: Colors.orange,
),
Container(
color: Colors.lightGreen,
),
Container(
color: Colors.red,
),
][_tabController.animation.value.round()],
);
},
);
}
_bottomTabBar() {
return TabBar(
controller: _tabController,
labelColor: Colors.black,
tabs: [
Tab(
icon: new Icon(Icons.home),
),
Tab(
icon: new Icon(Icons.public),
),
Tab(
icon: new Icon(Icons.group),
),
Tab(
icon: new Icon(Icons.person),
)
],
);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(title: const Text('Bottom App Bar')),
body: _tabBarView(),
bottomNavigationBar: _bottomTabBar(),
),
);
}
}
Screenshot (Null safe):
Code:
If you want fine-grained control, you can make use of the AnimationController.
class _MyPageState extends State<MyPage> with TickerProviderStateMixin {
late final TabController _tabController;
late final AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
value: 1,
);
_tabController = TabController(
length: 3,
vsync: this,
)..addListener(() {
if (_tabController.indexIsChanging) {
setState(() => _controller.forward(from: 0.5));
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ScaleTransition(
scale: _controller,
child: [
Container(color: Colors.red),
Container(color: Colors.green),
Container(color: Colors.blue),
][_tabController.index],
),
bottomNavigationBar: TabBar(
controller: _tabController,
tabs: [
Tab(child: Text('Red')),
Tab(child: Text('Green')),
Tab(child: Text('Blue')),
],
),
);
}
}
I don't know if you want to completely change the animation.
But if you just need some customization, did you try to use a TabController instead of a DefaultTabController ?
You just need to pass the tabController as an arg to the TabBar & TabBarView.
To customize the animation with the tabController, you should specify an Animation for the tabController and also specify the curve and duration with the animateTo function of the tabController.
https://api.flutter.dev/flutter/material/TabController/animateTo.html
https://api.flutter.dev/flutter/material/TabController-class.html
Disable animation between flutter tabs by setting animation duration to zero like this
tabController = TabController(
animationDuration: Duration.zero,
length: 4, vsync: this, initialIndex: 0);
Thank me later.