initState function is't be called in StatefulWidget by default - flutter

Thanks for your attention. I'm a beginner of flutter. I don't know why the initState function isn't called by default. Because of the print(list[0]) statement is not be run.
import 'package:flutter/material.dart';
import 'main_page/main_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() => _MyHomePage();
}
class _MyHomePage extends State<MyHomePage> {
int _currentIndex = 0;
List<Widget> list = List();
#override
void initState() {
list.add(MainPage());
print(list[0]);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: MainPage(),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home')
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
title: Text('Me')
),
],
currentIndex: _currentIndex,
onTap: (int index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
),
);
}
}

I tried your code and it still printed as normal. Please make sure you RE-RUN the code, don't do hot reloading since initState() is called only once. The document says:
The framework will call this method exactly once for each [State] object it creates.
One thing I pick from the documentation of initState() that you should follow:
If you override this, make sure your method starts with a call to super.initState().
That means you have to put all code under super.initState(), like below:
#override
void initState() {
super.initState();
list.add(MainPage());
print('initState() ---> ${list[0]}'); // This will print "initState() ---> MainPage"
}

Related

How to Make flutter webview load only once?

when I switch from one page to another the state of my WebView isn't remembered and it reloads every time. how can prevent that?
I was expecting the web view to load only for the first time and remember it's state but it gets reloaded every time i navigate to that screen. how to make it remember the state?
You can achieve this with PageView with AutomaticKeepAliveClientMixin.
See this example app:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final bucket = PageStorageBucket();
MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
final PageController _controller = PageController();
int _selectedIndex = 0;
void _onItemTapped(int index) {
setState(() {
_controller.jumpToPage(index);
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
// selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'home'),
BottomNavigationBarItem(icon: Icon(Icons.web), label: 'web'),
]),
body: PageView(
controller: _controller,
children: const [MyDummyPage(), MyWebView()],
),
);
}
}
class MyDummyPage extends StatefulWidget {
const MyDummyPage({super.key});
#override
State<MyDummyPage> createState() => _MyDummyPageState();
}
class _MyDummyPageState extends State<MyDummyPage>
with AutomaticKeepAliveClientMixin {
late int count;
#override
void initState() {
super.initState();
count = 0;
}
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('$count'),
],
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => setState(() {
count++;
}),
),
);
}
#override
bool get wantKeepAlive => true;
}
class MyWebView extends StatefulWidget {
const MyWebView({super.key});
#override
State<MyWebView> createState() => _MyWebViewState();
}
class _MyWebViewState extends State<MyWebView>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return const WebView(initialUrl: 'https://flutter.dev');
}
#override
bool get wantKeepAlive => true;
}

Navigation to sub page(nasted Navigation)

I love to design an app with features like the ones in this Gif
1-Bottom Navigation bar exists on every page(done)
2-pressing on the icon change only part of the page
My Main to move between pages
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int selectedPage = 2;
final _pageOptions = [const PageOne(),const PageTwo(),const PageThree()];
#override
Widget build(BuildContext context) {
return Scaffold(
body: _pageOptions[selectedPage],
bottomNavigationBar: BottomNavigationBar(
items:const [
BottomNavigationBarItem(icon: Icon(Icons.add), label: ""),
BottomNavigationBarItem(icon: Icon(Icons.abc), label: ""),
BottomNavigationBarItem(icon: Icon(Icons.access_alarm), label: ""),
],
onTap: (int index) {
setState(() {
selectedPage = index;
});
},
),
);
}
}
pressing on the icon change only part of the page
Thanks in advance
I used Tabbar in Appbar and use the body of scafoldto change the sub-page
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
var Pages=[const PageFour(),const PageFive()];
int pageNum=1;
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
initialIndex: 1,
child: Scaffold(
appBar: AppBar(
bottom:TabBar(
tabs: myTabs,
onTap: (value) {
setState(() {
pageNum=value;
});
}
),
),
),
)
body: Pages[pageNum],}
I found some solutions using Getx or auto-router but it was difficult to understand it for me as a beginner I hope this answer helps whoever facing the same problem despite I didn't know how to navigate using the page itself as shown in Gif if anyone knows how to do that please answer below.

didUpdateWidget called immediately after initState in Flutter

According to Flutter Documentation:
didUpdateWidget called whenever the widget configuration changes
But, in the following code, didUpdateWidget is called immediately after initState on the first time.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Test(),
);
}
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
#override
void initState() {
print("initState called");
super.initState();
}
#override
void didUpdateWidget(Test oldWidget) {
print("didUpdateWidget called");
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
return Container();
}
}
// output
//
// initState called
// didUpdateWidget called
Can someone describe why this happens? and how can I compare the whole oldWidget with widget
Thank you
update
as #pskink mentioned, didUpdateWidget is not called immediately after initState, it's after the first build
Yet another question is why it is called after the first build with the following code:
print("didUpdateWidget called"); <--
super.didUpdateWidget(oldWidget); <--
but if I call print after super.didUpdateWidget(oldWidget);, it works fine.
In my test using Flutter 2.10.1 stable version, didUpdateWidget was only called after calling setState() to update the data displayed on the Widget. This behavior matches the description mentioned on the docs where the didUpdateWidget method is called whenever the widget configuration changes. After the didUpdateWidget method was called, build will then be called.
Here's how the flow can look like after calling setState() once.
initState()
build()
setState() called
didUpdateWidget()
build()
This can be replicated on this simple Flutter app.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
void initState() {
debugPrint('State initState');
super.initState();
}
#override
void didUpdateWidget(MyHomePage oldWidget) {
debugPrint('State didUpdateWidget');
super.didUpdateWidget(oldWidget);
}
#override
Widget build(BuildContext context) {
debugPrint('State Widget build');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

Async Listview update in flutter

I have created a program which displays a list view in build method and in init I have a async method.
That async method after 3 seconds adds an element in list and try to set State.
It is not working. My code is as follows.
calling async function in init may be wrong, i want to show the List view then make an async http call and then update the list view. and this should work even after push and pop.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> europeanCountries = [
'Albania',
'Andorra',
'Armenia',
'Austria',
'Azerbaijan',
'Belarus',
'Belgium',
'Bosnia and Herzegovina'
];
int _counter = 0;
void _incrementCounter() async {
const ThreeSec = const Duration(seconds: 3);
Timer(ThreeSec, () {
europeanCountries.insert(0, "Testing");
print(europeanCountries);
});
setState(() {
_counter++;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
_incrementCounter();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _myListView(context),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _myListView(BuildContext context) {
// backing data
return ListView.builder(
itemCount: europeanCountries.length,
reverse: true,
itemBuilder: (context, index) {
return ListTile(
title: Text(europeanCountries[index]),
);
},
);
}
}
Use timeout method handle and call setState method inside that method like following way
Timer(ThreeSec, () {
europeanCountries.insert(0, "Testing");
print(europeanCountries);
setState(() {
_counter++;
});
});

Flutter: How to pass data from the parent stateful widget to one of the tabs in the BottomNavigationBar?

import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
HomeScreen();
#override
_HomeScreenState createState() => new _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _currentIndex = 0;
final List<Widget> _children = [
MapsScreen(),
HistoryScreen(),
];
#override
void initState() {
super.initState();
RestAPI.loadMapsFromNetwork();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home screen'),
),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped, // new
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.map),
title: new Text('Maps'),
),
BottomNavigationBarItem(
icon: new Icon(Icons.change_history),
title: new Text('History'),
)
],
),
);
}
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
}
This home.dart makes a network call in initState method.
How do I pass the list of maps that the client received from the network to one of the tabs like MapsScreen? Do I need to use ScopedModel or InheritedWidget or is there a better approach? All the logic to render is within the MapsScreen class.
You can pass the value from the json response like this.
class MapScreen extends StatefulWidget {
Map<List<String,dynamic>> data ;
MapScreen({this.data}) ;
_MapScreenState createState() => _MapScreenState() ;
}
class _MapScreenState extends State<MapScreen> {
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
/* use the data over here */
),
);
}
}