Last active
November 17, 2023 20:03
-
-
Save leighajarett/9482d760219459c837d2e56f1d37c7b5 to your computer and use it in GitHub Desktop.
Animated icons in nested nav tab bar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() => runApp(MyApp()); | |
// More netural / iOS friendly theme | |
final ColorScheme iosColorScheme = ColorScheme( | |
brightness: Brightness.light, | |
primary: Color(0xFF007AFF), // iOS blue | |
onPrimary: Colors.white, // Text color on primary color (typically white) | |
secondary: Color(0xFFF8F8F8), // Lighter blue for secondary elements | |
onSecondary: Colors.black, // Text color on secondary color (typically white) | |
error: Colors.red, // Default Material Design error color | |
onError: Colors.white, // Text color on error color | |
background: Color(0xFFF8F8F8), // Light background color (off white) | |
onBackground: Colors.black, // Text color on background color | |
surface: Colors.white, // Card and dialog background color | |
onSurface: Colors.black, // Text color on surface color | |
); | |
final ThemeData iosTheme = ThemeData.from(colorScheme: iosColorScheme); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
theme: iosTheme, | |
home: Root(), | |
); | |
} | |
} | |
// Create a concise way to store information for the tabs | |
class TabItem { | |
final int index; | |
final IconData icon; | |
final Widget page; | |
TabItem({required this.index, required this.icon, required this.page}); | |
} | |
// Example with two tabs | |
final List<TabItem> tabItems = [ | |
TabItem(index: 0, icon: Icons.home, page: Home(destinationIndex: 0)), | |
TabItem(index: 1, icon: Icons.business, page: Center(child: Text('Business Page'))), | |
// Add more TabItem entries for additional tabs | |
]; | |
class Root extends StatefulWidget { | |
@override | |
State<Root> createState() => _RootState(); | |
} | |
class _RootState extends State<Root> with TickerProviderStateMixin { | |
late TabController _tabController; | |
late List<GlobalKey<NavigatorState>> _navigatorKeys; | |
@override | |
void initState() { | |
super.initState(); | |
_navigatorKeys = List.generate(tabItems.length, (index) => GlobalKey<NavigatorState>()); | |
_tabController = TabController(length: tabItems.length, vsync: this); | |
} | |
@override | |
void dispose() { | |
_tabController.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: TabBarView( | |
controller: _tabController, | |
children: tabItems.map((tabItem) { | |
return Navigator( | |
key: _navigatorKeys[tabItem.index], | |
onGenerateRoute: (settings) => MaterialPageRoute( | |
builder: (context) => tabItem.page, | |
), | |
); | |
}).toList(), | |
), | |
bottomNavigationBar: TabBar( | |
indicatorColor: Colors.transparent, | |
overlayColor: MaterialStateProperty.all(Colors.transparent), | |
controller: _tabController, | |
tabs: tabItems.map((tabItem) => CustomTab(tabItem: tabItem, tabController: _tabController)).toList(), | |
), | |
); | |
} | |
} | |
class CustomTab extends StatefulWidget { | |
final TabItem tabItem; | |
final TabController tabController; | |
const CustomTab({Key? key, required this.tabItem, required this.tabController}) : super(key: key); | |
@override | |
_CustomTabState createState() => _CustomTabState(); | |
} | |
class _CustomTabState extends State<CustomTab> { | |
bool isSelected = false; | |
@override | |
void initState() { | |
super.initState(); | |
isSelected = widget.tabController.index == widget.tabItem.index; | |
widget.tabController.addListener(() { | |
bool newSelected = widget.tabController.index == widget.tabItem.index; | |
if (isSelected != newSelected) { | |
setState(() { | |
isSelected = newSelected; | |
}); | |
} | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return AnimatedScale( | |
scale: isSelected ? 1.5 : 1.0, | |
duration: const Duration(milliseconds: 300), | |
child: Icon( | |
widget.tabItem.icon, | |
), | |
); | |
} | |
} | |
class Home extends StatelessWidget { | |
final int destinationIndex; | |
const Home({Key? key, required this.destinationIndex}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: Text('Home Page')), | |
body: Center( | |
child: ElevatedButton( | |
child: Text('Go to List Page'), | |
onPressed: () { | |
Navigator.of(context).push(MaterialPageRoute( | |
builder: (context) => ListPage(), | |
)); | |
}, | |
), | |
), | |
); | |
} | |
} | |
class ListPage extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: Text('List Page')), | |
body: Center( | |
child: Text('Welcome to List Page'), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment