I want to add a button to ‘src/screens/home.dart’ that has the same transition as the BottomNavigationBar in ‘src/app.dart’. Of course, keeping the AppBar and BottomNavigationBar.
In my code, if I move from the button in home to cam Screen, it leaves app.dart.
It is also plausible that the UI should be reviewed.
After that transition, I want the BottomNavigationBar icon to light up. When moving to a screen with no icons, I want all icons to be dimmed.
Same as the ‘app.dart _buildIcon’ process.
I started learning Flutter yesterday, so if there’s any weird code, please let me know.
Thank you.
src/app.dart
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'screens/home.dart';
import 'screens/cam.dart';
import 'screens/setting.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
home: const BasePage(),
);
}
}
enum TabItem {
home(
title: 'home',
icon: Icons.home,
color: Colors.green,
page: HomeScreen()
),
cam(
title: 'cam',
icon: Icons.video_camera_back,
color: Colors.lightGreen,
page: CamScreen()
),
setting(
title: 'setting',
icon: Icons.settings,
color: Colors.cyan,
page: SettingScreen()
);
const TabItem({
required this.title,
required this.icon,
required this.color,
required this.page,
});
final String title;
final IconData icon;
final Color color;
final Widget page;
}
final _navigatorKeys = <TabItem, GlobalKey<NavigatorState>>{
TabItem.home: GlobalKey<NavigatorState>(),
TabItem.cam: GlobalKey<NavigatorState>(),
TabItem.setting:GlobalKey<NavigatorState>(),
};
class BasePage extends HookWidget {
const BasePage({super.key});
@override
Widget build(BuildContext context) {
final currentTab = useState(TabItem.home);
return Scaffold(
appBar: AppBar(
toolbarHeight: MediaQuery.of(context).size.height / 16,
title: Image.asset('assets/images/logo.png', width: 200, fit: BoxFit.contain,),
centerTitle: true,
),
body: Stack(
children: TabItem.values.map(
(tabItem) => Offstage(
offstage: currentTab.value != tabItem,
child: Navigator(
key: _navigatorKeys[tabItem],
onGenerateRoute: (settings) {
return MaterialPageRoute<Widget>(
builder: (context) => tabItem.page,
);
},
),
),
).toList(),
),
bottomNavigationBar: SizedBox(
height: MediaQuery.of(context).size.height /8,
child:BottomNavigationBar(
currentIndex: TabItem.values.indexOf(currentTab.value),
selectedFontSize: 0,
unselectedFontSize: 0,
onTap: (index) {
final selectedTab = TabItem.values[index];
if (currentTab.value == selectedTab) {
_navigatorKeys[selectedTab]
?.currentState
?.popUntil((route) => route.isFirst);
} else {
currentTab.value = selectedTab;
}
},
items: TabItem.values.map(
(tabItem) => BottomNavigationBarItem(
icon: _buildIcon(tabItem.icon, tabItem.color, selected: currentTab.value==tabItem),
label: tabItem.title,
),
).toList(),
type: BottomNavigationBarType.fixed,
),
),
);
}
Widget _buildIcon(IconData icon, Color color, {bool selected=false}) {
Color filteredColor = selected ? color : color.withOpacity(0.3);
return Container(
width: 60, height: 60,
decoration: BoxDecoration(shape: BoxShape.circle, color: filteredColor),
child: Icon(icon, size: 50, color: Colors.white,),
);
}
}
src/screens/home.dart
import 'package:flutter/material.dart';
import 'cam.dart';
import 'setting.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: GestureDetector(
onTap: (){Navigator.push(context, MaterialPageRoute(builder: (context) => const CamScreen(),));},
child: Container(
// Omit the code here.
),
),
),
],
);
}
}
EDIT
src/app.dart
import 'package:flutter/material.dart';
import 'screens/home.dart';
import 'screens/cam.dart';
import 'screens/setting.dart';
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent,
),
);
}
@override
State createState() => BasePage();
}
enum TabItem {
home(
title: 'home',
icon: Icons.home,
color: Colors.green,
page: HomeScreen()
),
cam(
title: 'cam',
icon: Icons.video_camera_back,
color: Colors.lightGreen,
page: CamScreen()
),
setting(
title: 'setting',
icon: Icons.settings,
color: Colors.cyan,
page: SettingScreen()
);
const TabItem({
required this.title,
required this.icon,
required this.color,
required this.page,
});
final String title;
final IconData icon;
final Color color;
final Widget page;
}
final _navigatorKeys = <TabItem, GlobalKey<NavigatorState>>{
TabItem.home: GlobalKey<NavigatorState>(),
TabItem.cam: GlobalKey<NavigatorState>(),
TabItem.setting:GlobalKey<NavigatorState>(),
};
class BasePage extends State<MyApp> {
final _pageController = PageController();
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: MediaQuery.of(context).size.height / 16,
title: Image.asset('assets/images/logo.png', width: 200, fit: BoxFit.contain,),
centerTitle: true,
),
body: Expanded(
child: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
onPageChanged: (index){
setState(() {
_currentIndex = index;
});
},
children: TabItem.values.map(
(tabItem) => tabItem.page
).toList(),
),
),
bottomNavigationBar: SizedBox(
height: MediaQuery.of(context).size.height /8,
child:BottomNavigationBar(
currentIndex: _currentIndex,
selectedFontSize: 0,
unselectedFontSize: 0,
items: TabItem.values.map(
(tabItem) => BottomNavigationBarItem(
icon: _buildIcon(tabItem.icon, tabItem.color, selected: _currentIndex==tabItem.index),
label: tabItem.title,
),
).toList(),
onTap: (index) {
setState(() {
_currentIndex = index;
});
_pageController.jumpToPage(index);
//_pageController.animateToPage(index, duration: const Duration(0), curve: Curves.easeIn);
},
type: BottomNavigationBarType.fixed,
),
),
);
}
Widget _buildIcon(IconData icon, Color color, {bool selected=false}) {
Color filteredColor = selected ? color : color.withOpacity(0.3);
return Container(
width: 60, height: 60,
decoration: BoxDecoration(shape: BoxShape.circle, color: filteredColor),
child: Icon(icon, size: 50, color: Colors.white,),
);
}
}
2
Answers
Maybe you can give a try to
PageView
widget.Just wrap the PageView in a Expanded widget & wrap it with your appbar & bottom tabbar.
To change page, use the page view controller, the animation will be animated (can be disabled of course).
https://api.flutter.dev/flutter/widgets/PageView-class.html
Check out the below code. In your edited code you were handling MyApp widget incorrectly, you need to pass a home widget to MaterialApp.
And you don’ need onPageChange here beacuse it is used when you scroll in PageView and want to do some changes on page change but in your case you are manually changing it on bottom navigation bar click