build method of subclass that extends RouterDelegate is not rebuild when App's state changed in Flutter - flutter

When app is open,the first_screen widget must display until 3seconds and after the time passed home_page widget must show that I expect.But,after over 3seconds,home_page is not appear.State changing is notified from provider to Router.Which part is error?Please!!
My router app_route.dart is
import 'package:flutter/material.dart';
import 'package:navigator_2/keys/screen_keys.dart';
import 'package:navigator_2/provider/tab_provider.dart';
import 'package:navigator_2/root_screen.dart';
import 'package:navigator_2/screens/first_screen.dart';
class AppRoute extends RouterDelegate<AppKey> with ChangeNotifier,PopNavigatorRouterDelegateMixin{
#override
late final GlobalKey<NavigatorState> navigatorKeys;
final TabManager _tabManager;
AppRoute(this._tabManager) : navigatorKeys = GlobalKey<NavigatorState>(){
_tabManager.addListener(() {notifyListeners();});
}
#override
Widget build(BuildContext context) {
print("${_tabManager.isInitialPage}");
print("${_tabManager.currentTab}");
return Navigator(
key: navigatorKeys,
onPopPage: _handlePopPage,
pages: [
if(!_tabManager.isInitialPage) FirstScreen.page(),
if(_tabManager.isInitialPage == true)...{
RootScreen.page(_tabManager.currentTab)
},
],
);
}
#override
Future<void> setNewRoutePath(configuration) {
// TODO: implement setNewRoutePath
throw UnimplementedError();
}
bool _handlePopPage(Route<dynamic> route, result) {
if(!route.didPop(result)){
return false;
}
return true;
}
#override
// TODO: implement navigatorKey
GlobalKey<NavigatorState>? get navigatorKey => navigatorKeys;
}`
--------------------------------------------------------------------------------------------------
main.dart is
import 'package:flutter/material.dart';
import 'package:navigator_2/provider/tab_provider.dart';
import 'package:navigator_2/router_api/app_route.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late AppRoute _appRoute;
TabManager tabManager = TabManager();
#override
void initState() {
super.initState();
_appRoute = AppRoute(tabManager);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => TabManager())
],
child: Router(routerDelegate: _appRoute),
),
);
}
}

Related

How to Call Method of statefull Widget out side of the stateful widget

I Create stateful Widget Called Globals, (like below code) and I have a method ChatsCounter() inside this stateful Widget, how to Call this method inside main.dart.out side of stateful Widget,
I want to Call ChatsCounter(); inside maind.dart (like below.)
import 'package:flutter/material.dart';
void main() {
FirebaseMessaging.onMessage.listen((message) {
ChatsCounter(); // I want to Call here
});
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: "Taxiyee_Messaging_app",
home: Container(),
),
);
}
**Here is my StatefulWidget :**
import 'package:flutter/material.dart';
class Globals extends StatefulWidget {
const Globals({Key? key}) : super(key: key);
#override
_GlobalsState createState() => _GlobalsState();
}
class _GlobalsState extends State<Globals> {
#override
Widget build(BuildContext context) {
return Container();
}
**//I want to call this method inside main.dart above**
ChatsCounter() {
setState(() {
counter++;
});
}
}
Try below code hope its help to you. create one final variable or var and call it as _GlobalsState() and pass it below.
import 'package:flutter/material.dart';
final globalState = _GlobalsState();
void main() {
globalState.chatsCounter();//call this way
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: "Taxiyee_Messaging_app",
home: Container(),
),
);
}
class Globals extends StatefulWidget {
const Globals({Key? key}) : super(key: key);
#override
_GlobalsState createState() => _GlobalsState();
}
class _GlobalsState extends State<Globals> {
int counter = 0;
#override
Widget build(BuildContext context) {
return Container();
}
chatsCounter() {
print('Method Call');
setState(() {
counter++;
});
}
}

Flutter GetX Refreshing Explorer restart controllers

I'm using GetX for state management in a Flutter web application. I have created an authcontroller with a login method. When I call the method anywhere in the application it works and the UI changes with the new state. But when I refresh explorer the controller is reset and login state is lost.
I have created a small version of the code easy to reproduce:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp( MyApp());
}
class MyApp extends StatelessWidget {
AuthController authController = Get.put(AuthController(), permanent: true);
MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
smartManagement: SmartManagement.keepFactory,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
HomeScreen({Key? key}) : super(key: key);
final AuthController authController = Get.find();
#override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
child: Obx(() => authController.isAuthenticated.value ? Text("authenticated") : Text("authenticate")),
onPressed: () {
authController.login();
},
)
);
}
}
class AuthController extends GetxController {
var isAuthenticated = false.obs;
void login() {
isAuthenticated.value = true;
update();
}
}
As you can see I'm using the permanent:true prop but the controller still is re-initialized.
This is how the issue looks:
Is there any prop or config that I'm missing? how to avoid this behavior?
The error in your code is that the Controller is recreated every time the page is refreshed. That is why Use GetStorage package.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() async {
await GetStorage.init(); // Add this first
runApp( MyApp());
}
class MyApp extends StatelessWidget {
AuthController authController = Get.put(AuthController(), permanent: true);
MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
smartManagement: SmartManagement.keepFactory,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
HomeScreen({Key? key}) : super(key: key);
final AuthController authController = Get.find();
#override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
child: _isLoged.read('login') == false ||
_isLoged.read('login') == null ? Text("authenticate")
: Text("authenticated") ,
onPressed: () {
authController.login();
},
)
);
}
}
class AuthController extends GetxController {
var _isLoged = GetStorage(); // In the controller, it is necessary to save the entry with this
void login() async {
await _isLoged.write('login', true);
update();
}
}

How do I add Widgets to a Column through code

I'm trying to add widgets to a Column dynamically. The following approach does not work as the button does not add the text widgets when clicked. I'd like to understand why this doesn't work and what should I do to make it work. Thanks in advance for the help.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
List<Widget> c = [];
return MaterialApp(
title: 'Flutter Demo',
home: Column(children: [
Column(children: c),
ElevatedButton(onPressed: (){
print("testing");
c.add(Text("testing"));
}, child: Text("Add Text"))
])
);
}
}
Edit: I edited my code to be in a stateful widget and added a setState function around the function that adds the widgets to the container, but it still won't work
import 'package:flutter/material.dart';
class SW extends StatefulWidget {
const SW({ Key? key }) : super(key: key);
#override
_SWState createState() => _SWState();
}
class _SWState extends State<SW> {
#override
Widget build(BuildContext context) {
List<Widget> c = [];
return MaterialApp(
title: 'Flutter Demo',
home: Column(children: [
Column(children: c),
ElevatedButton(onPressed: (){
setState((){
print("testing");
c.add(Text("testing"));
});
},
child: Text("Add Text"))
])
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SW();
}
}
void main() {
runApp(MyApp());
}
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home:MyApp()));
class MyApp extends StatefulWidget {
#override
_MyState createState() => _MyState();
}
class _MyState extends State<MyApp> {
List<Widget> c = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body:Column(children: [
Column(children: c),
ElevatedButton(onPressed: (){
setState((){
print("testing");
c.add(Text("testing"));
});
}, child: Text("Add Text"))
]),
);
}
}
Wrap your on pressed code in setstate to rebuild the page. The setState function triggers rebuild of the whole widget tree shown in your screen. And without that the text widget will only be added to the list but won't be shown to the screen.

MediaQuery.of() called with a context that does not contain a media query. visual studio Code [duplicate]

I have been trying to get the size of the whole context view in Flutter. But every time I try I'm getting the above mentioned error.
Here's my code:
import 'package:flutter/material.dart';
void main => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return new MaterialApp(
home: new Scaffold(),
);
}
}
Note: I also tried with a StatefulWidget.
Please, help me find what I'm doing wrong here.
You need a MaterialApp or a WidgetsApp around your widget. They provide the MediaQuery. When you call .of(context) flutter will always look up the widget tree to find the widget.
You usually have this in your main.dart:
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
theme: kThemeData,
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Container(
child: ...,
);
}
}
What works for us is using WidgetsBinding.instance.window instead of MediaQuery - also when setting the theme of the MaterialApp:
_pixelRatio = WidgetsBinding.instance.window.devicePixelRatio;
_screenWidth = WidgetsBinding.instance.window.physicalSize.width;
_screenHeight = WidgetsBinding.instance.window.physicalSize.height;
_statusBarHeight = WidgetsBinding.instance.window.padding.top;
_bottomBarHeight = WidgetsBinding.instance.window.padding.bottom;
_textScaleFactor = WidgetsBinding.instance.window.textScaleFactor;
You can access MediaQuery when you are inside MaterialApp. The place where you are accessing the media query is not correct.
Please refer below code:
import 'package:flutter/material.dart';
class CommonThings {
static Size size;
}
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'MediaQuery Demo',
theme: new ThemeData(
primarySwatch: Colors.red,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
CommonThings.size = MediaQuery.of(context).size;
print('Width of the screen: ${CommonThings.size.width}');
return new Container();
}
}
I've purposely created a class CommonThings which has static Size so that you can use it throughout the app.
I fixed it by using the following method. First I created a new class named MyWidget and returned it in MyApp within a MaterialApp's home:. Refer code below:
import 'package:flutter/material.dart';
void main => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyWidget(),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return new MaterialApp(
home: new Scaffold(),
);
}
}
Also, declaring size as final doesn't matter. Orientation/Rotation is handled.
Solved by re-run the app(click on stop button in android studio then run again)
There is better way. Above solutions would require you to have only one screen widget or inherit all screens from parent class. But there is solution, place the media query initialization into onGenerateRoute callback function
main.dart
import 'package:flutter/material.dart';
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => new MyAppState();
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Awesome App',
routes: NavigationUtils.routeList(),
onGenerateRoute: (routeSettings) =>
NavigationUtils.onGenerateRoute(routeSettings),
);
}
}
NavigationUtils.dart
import 'package:flutter/material.dart';
class NavigationUtils {
static onGenerateRoute(RouteSettings routeSettings) {
return new MaterialPageRoute(
builder: (context) {
WidgetUtils.me.init(context);
return StorageUtils.me.isLogged() ? HomeScreen() : ForkScreen();
},
settings: routeSettings,
);
}
}
WidgetUtils.dart
import 'package:flutter/material.dart';
class WidgetUtils {
MediaQueryData _mediaQueryData;
double _screenWidth;
double _screenHeight;
double _blockSizeHorizontal;
double _blockSizeVertical;
init(BuildContext context) {
_mediaQueryData = MediaQuery.of(context);
screenWidth = _mediaQueryData.size.width;
screenHeight = _mediaQueryData.size.height;
blockSizeHorizontal = screenWidth / 100;
blockSizeVertical = screenHeight / 100;
}
}
Warning: It is not copy & paste code, there are some singletons etc. but you should get the point ;)
Had the same error in
import 'screens/tasks_screen.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return TasksScreen();
}
}
I solved it by:-
import 'package:flutter/material.dart';
import 'screens/tasks_screen.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: TasksScreen(),
);
}
}
Wrap your code in a Material App widget. I also had the same issue as I forgot to use it and directly returned the scaffold.
In other words, your MediaQuery.of(context) should be inside the Material Widget.
Material app -> scaffold -> MediaQuery.of(context)
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyAppOne(),
);
}
}
class MyAppOne extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyAppOne>{
#override
Widget build(BuildContext context){
return Scaffold(
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body:HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size.height;
return Container(
height:size/2,
color:Colors.lightBlueAccent,
);
}
}
YOU SHOULD TRY THIS I HAVE DONE IT.
I was trying to change the package then this error arise,
so make sure you complete each of the following steps
https://stackoverflow.com/a/51550358/4993045
Add MaterialApp ...
void main() {
runApp(MaterialApp(
home: HomePage(),
));
}

Flutter : SharedPreferences not fetching value at app start

I am trying to store a value and based on the value I want to navigate to LandinPage or HomePage. However when my app loads I am not able to get the SharedPreferences value. Currently, the value is set on click of a button in Landing page, and when I close/minimize the app. I don't even get to see the print messages from main.dart and can't fetch values. What am I doing wrong?
Here is my code:
import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<MyApp> {
#override
void initState() {
super.initState();
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return LandingPage();
}));
} else {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return HomePage();
}));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}
You may need to use a "loading page" that is first loaded before any of your two pages:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.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: 'An App',
home: LoadingPage(),
routes: {
'/landing': (context) => LandingPage(),
'/home': (context) => HomePage(),
}
);
}
}
class LoadingPage extends StatefulWidget {
LoadingPage({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> {
#override
void initState() {
super.initState();
loadPage();
}
loadPage() {
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context).pushNamed('/landing');
} else {
Navigator.of(context).pushNamed('/home');
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Container(
child: Text('Home Page'),
);
}
}
class LandingPage extends StatefulWidget {
LandingPage({Key key}) : super(key: key);
_LandingPageState createState() => _LandingPageState();
}
class _LandingPageState extends State<LandingPage> {
#override
void initState() {
super.initState();
setUserStatus('done');
}
#override
Widget build(BuildContext context) {
return Container(
child: Text('Landing'),
);
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userStatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}
Future<bool> setUserStatus(String userStatus) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('userStatus', userStatus);
return true;
}
You've declared a method main of MyApp but it never gets called. The main that starts the app is the one with runApp in it. You could move the prefs.getString() into the real main (having made it async) and then pass the value into the MyApp widget as a parameter.
I feel like Willie's answer may be just as good, but here's another approach.
Overall, my approach would be to load the main home page automatically, and then in the initstate of the home page, check to see if this is the user's first visit to the app. If so, pop the landing page on top immediately. I've used this approach successfully without the user having a poor experience.
Below is the default app but with your SharedPreferences code moved to the appropriate spot.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
var userStatus;
//If user status is null, then show landing page.
Future<void> checkUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
if (userStatus == null) {
Navigator.push(context, MaterialPageRoute(builder: (context) => LandingPage()));
}
}
#override
void initState() {
super.initState();
//Call check for landing page in init state of your home page widget
checkUserStatus();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class LandingPage extends StatefulWidget {
#override
_LandingPageState createState() => _LandingPageState();
}
class _LandingPageState extends State<LandingPage> {
#override
Widget build(BuildContext context) {
//Build landing page here.
return Container();
}
}
I know this question is old and already been answered but for my situation, Richard Heap's answer was more suitable so I would like to add a code snippet for others.
I only cite part of it, so please modify it if you are going to use it for your app. After the Landing/Welcome page is viewed by user, update the preference by setBool and it won't show up after that.
void main() async {
// do whatever
SharedPreferences prefs = await SharedPreferences.getInstance();
bool hideWelcome = prefs.getBool('hideWelcome') ?? false;
// start your app
runApp(MyApp(hideWelcome));
}
class MyApp extends StatelessWidget {
final hideWelcome;
MyApp(this.hideWelcome);
#override
Widget build(BuildContext context) {
return MaterialApp(
// other setting like theme, title
initialRoute: hideWelcome ? '/' : '/welcome',
routes: {
'/': (context) => MyHomePage(),
'/welcome': (context) => WelcomePage(),
// other pages
}
);
}
you must add
#override
void initState() {
getUserStatus();
super.initState();
}
var name;
void getUserStatus() async {
SharedPreferences prefs= await SharedPreferences.getInstance();
setState(() {
userStatus = prefs.getString("userStatus");
});
}