Here is the substance of my main.dart file:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:webview_flutter/webview_flutter.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Home()),
ChangeNotifierProvider(create: (_) => _HomeState()),
ChangeNotifierProvider(create: (_) => _SettingsPageState()),
],
child: MaterialApp(
title: 'My Great App',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: Home(),
),
),
);
}
class BottomNavBarItemData {
String label;
IconData icon;
BottomNavBarItemData(this.label, this.icon);
}
class PageViewData {
Widget page;
PageViewData(this.page);
}
//The Main Page Class
class Home extends StatefulWidget with ChangeNotifier {
Home({super.key});
int _selectedIdx = 0;
int get selectedIdx => _selectedIdx;
late Widget _body;
void updateSelectedIndex(index) {
print('This is _selectedIdx:');
print(_selectedIdx);
_selectedIdx = index;
print('This is now _selectedIdx:');
print(_selectedIdx);
print('This is selectedIdx:');
print(selectedIdx);
notifyListeners();
}
final List<BottomNavBarItemData> navIcons = [
BottomNavBarItemData("Home", Icons.cottage),
BottomNavBarItemData("Settings", Icons.settings),
];
final List<PageViewData> pageScreens = [
PageViewData(const HomePage()),
PageViewData(SettingsPage()),
PageViewData(const SettingsPage2()),
];
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> with ChangeNotifier {
final pageTitles = ['My Home', 'My Settings', 'My Settings 2'];
int get thisSelectedIdx => widget._selectedIdx;
@override
void initState() {
print('Init State for Home');
super.initState();
}
@override
Widget build(BuildContext context) {
print('This is the top thisSelectedIdx:');
print(thisSelectedIdx);
if (thisSelectedIdx == 2) {
print('The index is now 2!');
widget._body = IndexedStack(
index: widget._selectedIdx,
children: const [
SettingsPage2(),
],
);
} else {
widget._body = IndexedStack(
index: widget._selectedIdx,
children: [
...widget.pageScreens.map((e) => e.page).toList(),
],
);
}
return Scaffold(
appBar: TopAppBar(appBarTitle: pageTitles[widget._selectedIdx]),
body: widget._body,
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: widget._selectedIdx,
onTap: (idx) => setState(() {
widget._selectedIdx = idx;
Home().updateSelectedIndex(widget._selectedIdx);
print('This is widget._selectedIdx:');
print(widget._selectedIdx);
print('This is thisSelectedIdx:');
print(thisSelectedIdx);
}),
items: widget.navIcons
.map(
(e) => BottomNavigationBarItem(
label: e.label,
icon: Icon(e.icon),
),
)
.toList(),
showSelectedLabels: true,
showUnselectedLabels: true,
backgroundColor: Colors.black,
selectedItemColor: const Color.fromARGB(255, 0, 255, 0),
unselectedItemColor: Colors.grey,
),
);
}
}
//The Reload Button Widget
class ReloadButton extends StatelessWidget {
const ReloadButton({super.key});
@override
Widget build(BuildContext context) {
return IconButton(
color: const Color.fromARGB(255, 0, 255, 0),
icon: const Icon(Icons.refresh),
tooltip: 'Refresh the Page',
onPressed: () {
print('Page Refreshed');
},
);
}
}
//The App Bar Class
class TopAppBar extends StatefulWidget implements PreferredSizeWidget {
const TopAppBar(
{super.key,
required this.appBarTitle,
this.preferredSize = const Size.fromHeight(50)});
final String appBarTitle;
@override
final Size preferredSize;
@override
State<TopAppBar> createState() => _TopAppBarState();
}
class _TopAppBarState extends State<TopAppBar> {
@override
void initState() {
print('Init State for TopAppBar');
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return AppBar(
foregroundColor: const Color.fromARGB(255, 0, 255, 0),
title: Text(widget.appBarTitle),
actions: const <Widget>[
ReloadButton(), //IconButton
], //<Widget>[]
backgroundColor: Colors.black,
automaticallyImplyLeading: false,
);
}
}
//The Home Page Class
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late final _controller = WebViewController();
int indexPosition = 0;
beginLoading(String A) {
if (!mounted) return;
setState(() {
indexPosition = 1;
});
}
completeLoading(String A) {
if (!mounted) return;
setState(() {
indexPosition = 0;
});
}
@override
void initState() {
print('Init State for Home Page');
_controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..clearCache()
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String url) {
beginLoading(url);
},
onPageFinished: (String url) {
completeLoading(url);
},
onWebResourceError: (WebResourceError error) {},
),
)
..loadRequest(Uri.parse('https://www.flutter.dev'));
super.initState();
}
@override
void dispose() {
super.dispose();
_controller;
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
padding: const EdgeInsets.only(left: 5.0, right: 5.0),
child: IndexedStack(
index: indexPosition,
children: <Widget>[
WebViewWidget(controller: _controller),
Container(
color: Colors.black,
child: const Center(child: CircularProgressIndicator()),
),
],
),
);
}
}
//The Settings Page Class
class SettingsPage extends StatefulWidget with ChangeNotifier {
SettingsPage({super.key});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> with ChangeNotifier {
bool _thisOptionIsChecked = false;
bool _thisOption = false;
String _thisOptionText = 'Off';
@override
void initState() {
print('Init State for Settings Page');
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
Color getColor(Set<MaterialState> states) {
const Set<MaterialState> interactiveStates = <MaterialState>{
MaterialState.selected,
MaterialState.focused,
MaterialState.pressed,
};
if (states.any(interactiveStates.contains)) {
return Colors.green;
}
return Colors.red;
}
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Center(
child: Container(
color: Colors.black,
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
width: 180,
child: Column(
children: const <Widget>[
Text(
'This Option',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
),
SizedBox(
width: 50,
child: Column(
children: <Widget>[
Checkbox(
checkColor: Colors.white,
fillColor:
MaterialStateProperty.resolveWith(getColor),
value: _thisOptionIsChecked,
onChanged: (bool? value) {
if (!mounted) return;
setState(() {
_thisOptionIsChecked = value!;
_thisOption = value;
if (_thisOption == true) {
_thisOptionText = 'On';
} else {
_thisOptionText = 'Off';
}
});
},
),
],
),
),
SizedBox(
width: 75,
child: Column(
children: <Widget>[
Text(
_thisOptionText,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
),
SizedBox(
width: 50,
child: Column(
children: const <Widget>[
Text(''),
],
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 35,
width: 120,
child: TextButton(
style: TextButton.styleFrom(
textStyle: const TextStyle(
fontSize: 11, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
backgroundColor: Colors.grey,
foregroundColor: Colors.black,
),
onPressed: () {
int index = 7;
Provider.of<Home>(context, listen: false)
.updateSelectedIndex(index);
notifyListeners();
print('Open Location Settings');
},
child: const Text('My Settings 2'),
),
),
],
),
],
),
),
);
});
}
}
class SettingsPage2 extends StatefulWidget {
const SettingsPage2({super.key});
@override
State<SettingsPage2> createState() => _SettingsPage2State();
}
class _SettingsPage2State extends State<SettingsPage2> {
late var _controller = WebViewController();
int indexPosition = 0;
beginLoading(String A) {
if (!mounted) return;
setState(() {
indexPosition = 1;
});
}
completeLoading(String A) {
if (!mounted) return;
setState(() {
indexPosition = 0;
});
}
@override
void initState() {
print('Init State for My Settings Page 2');
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String url) {
beginLoading(url);
},
onPageFinished: (String url) {
completeLoading(url);
},
onWebResourceError: (WebResourceError error) {},
),
)
..loadRequest(Uri.parse('https://www.google.com'));
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
padding: const EdgeInsets.only(left: 5.0, right: 5.0),
child: IndexedStack(
index: indexPosition,
children: <Widget>[
WebViewWidget(
controller: _controller,
),
Container(
color: Colors.black,
child: const Center(child: CircularProgressIndicator()),
),
],
),
);
}
}
I only have the two pages, Home and Settings, loading in my navigation menu because I want the second page of settings to remain hidden until the user taps on Settings and then presses the My Settings 2 button.
I am a bit lost using the ChageNotifier provider, which I think is what I need, and I am not sure what else to try. The main page loads with a web page and works great. The Settings page opens and works great as well. On that page, I have a button which, when pressed, should open up another page in the main window. I am using the selectedIdx and _selectedIdx variables to track movement between the pages. When I click on Home or Settings in the bottomNavigationBar, everything prints appropriately. I get the printed output I would expect:
flutter: This is _selectedIdx:
flutter: 0
flutter: This is now _selectedIdx:
flutter: 0
flutter: This is selectedIdx:
flutter: 0
flutter: This is widget._selectedIdx:
flutter: 0
flutter: This is thisSelectedIdx:
flutter: 0
flutter: This is the top thisSelectedIdx:
flutter: 0
flutter: This is _selectedIdx:
flutter: 0
flutter: This is now _selectedIdx:
flutter: 1
flutter: This is selectedIdx:
flutter: 1
flutter: This is widget._selectedIdx:
flutter: 1
flutter: This is thisSelectedIdx:
flutter: 1
flutter: This is the top thisSelectedIdx:
flutter: 1
However, when I click on the My Settings 2 button, it is not behaving the way I would expect it to. It does call the Provider.of<Home>(context, listen: false).updateSelectedIndex(index)
function and passed the index of 2, but it just dies there. I get the following:
flutter: This is _selectedIdx:
flutter: 0
flutter: This is now _selectedIdx:
flutter: 2
flutter: This is selectedIdx:
flutter: 2
flutter: Open Settings Page 2
What I would expect to see is this:
flutter: This is _selectedIdx:
flutter: 0
flutter: This is now _selectedIdx:
flutter: 2
flutter: This is selectedIdx:
flutter: 2
flutter: This is widget._selectedIdx:
flutter: 2
flutter: This is thisSelectedIdx:
flutter: 2
flutter: This is the top thisSelectedIdx:
flutter: 2
flutter: Open Settings Page 2
This tells me that the Home class is not being rebuilt. So, even though the function runs, I never get an updated widget for the Home class. I am racking my brain on how to get this button to show the SettingsPage2 class. Any ideas to help get me unstuck? I thought this would be fairly simple to do, but it just doesn't work the way my mind thinks it should.