Skip to content

Instantly share code, notes, and snippets.

@daveshirman
Created June 9, 2022 15:54
Show Gist options
  • Save daveshirman/95e0d2158d518965a8aebb817c79b50c to your computer and use it in GitHub Desktop.
Save daveshirman/95e0d2158d518965a8aebb817c79b50c to your computer and use it in GitHub Desktop.
Navigator changing colour of Scaffold beneath transparent widget
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: App(),
);
}
}
class App extends StatefulWidget {
@override
State<StatefulWidget> createState() => AppState();
}
class AppState extends State<App> {
// this is static property so other widget throughout the app
// can access it simply by AppState.currentTab
static int currentTab = 0;
// list tabs here
final List<TabItem> tabs = [
TabItem(
tabName: "Home 1".toUpperCase(),
page: PageHome(),
),
TabItem(
tabName: "Home 2".toUpperCase(),
page: PageHome(),
),
];
AppState() {
// indexing is necessary for proper functionality
// of determining which tab is active
tabs.asMap().forEach((i, item) => item.setIndex(i));
}
@override
void initState() {
super.initState();
}
// sets current tab index
// and update state
void _selectTab(int index) {
if (index == currentTab) {
// pop to first route
// if the user taps on the active tab
tabs[index].key.currentState!.popUntil((route) => route.isFirst);
} else {
// update the state
// in order to repaint
if (mounted) {
currentTab = index;
_updateView();
}
}
}
void _updateView() {
if (mounted) {
setState(() {});
}
}
void resetToHomeTab() {
currentTab = 0;
}
@override
Widget build(BuildContext context) {
Widget body = Scaffold(
backgroundColor: Colors.transparent,
body: IndexedStack(
index: currentTab,
children: tabs.map((e) => e.page).toList(),
),
bottomNavigationBar: BottomNavigation(
onSelectTab: _selectTab,
tabs: tabs,
),
);
// Background of the tab view
Widget wrap = Container(
width: double.infinity,
height: double.infinity,
color: Colors.blue,
child: body,
);
// WillPopScope handle android back btn
return WillPopScope(
onWillPop: () async {
final isFirstRouteInCurrentTab =
!await tabs[currentTab].key.currentState!.maybePop();
if (isFirstRouteInCurrentTab) {
// if not on the 'main' tab
if (currentTab != 0) {
// select 'main' tab
_selectTab(0);
// back button handled by app
return false;
}
}
// let system handle back button if we're on the first route
return isFirstRouteInCurrentTab;
},
// this is the base scaffold
// don't put appbar in here otherwise you might end up
// with multiple appbars on one screen
// eventually breaking the app
child: wrap,
);
}
}
class TabItem {
final String tabName;
final GlobalKey<NavigatorState> key = GlobalKey<NavigatorState>();
int _index = 0;
Widget? _page;
TabItem({
required this.tabName,
required Widget page,
}) {
_page = page;
}
void setIndex(int i) {
_index = i;
}
int getIndex() => _index;
Widget get page {
// If you toggle between the two return statements below, you can see the transparency
// not working as expected when the page is wrapped in the Navigator
// return _page!;
return Visibility(
visible: (_index == AppState.currentTab),
maintainState: true,
// child: _page!,
child: Navigator(
key: key,
onGenerateRoute: (routeSettings) {
return CupertinoPageRoute(
builder: (_) {
return (_page != null) ? _page! : const SizedBox.shrink();
},
);
},
),
);
}
}
class BottomNavigation extends StatelessWidget {
const BottomNavigation({
required this.onSelectTab,
required this.tabs,
});
final ValueChanged<int> onSelectTab;
final List<TabItem> tabs;
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
elevation: 0,
showSelectedLabels: true,
showUnselectedLabels: true,
backgroundColor: Colors.transparent,
fixedColor: Colors.white,
type: BottomNavigationBarType.fixed,
items: tabs
.map((e) => _buildItem(e.getIndex(), e.tabName))
.toList(),
onTap: (index) => onSelectTab(
index,
),
currentIndex: AppState.currentTab,
);
}
BottomNavigationBarItem _buildItem(int index, String tabName) {
return BottomNavigationBarItem(
icon: const Padding(
padding: EdgeInsets.only(bottom: 4),
child: Icon(Icons.home),
),
label: tabName,
);
}
}
class PageHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const Scaffold(
backgroundColor: Colors.transparent,
body: Center(
child: Text("Home page"),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment