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

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++;
});
}
}

Related

Flutter - how to call child widget's method from parent

Let's say we have two stateful widgets.
ParentWidget(){
}
ChildWidget() {
someMethod(){
// some code, for example setState code
}
}
Now when I use the ChildWidget in ParentWidget, how do I call the someMethod()?
If you need call function on widget you can use:
context.findAncestorWidgetOfExactType<T>()
If you need call function on state of that widget you can use:
context.findRootAncestorStateOfType<T>();
read more at:
https://api.flutter.dev/flutter/widgets/BuildContext/findAncestorWidgetOfExactType.html
https://api.flutter.dev/flutter/widgets/BuildContext/findRootAncestorStateOfType.html
Here is way what I've used.
Make a GlobalKey instance
Pass the Globalkey as a Key parameter to child widget.
Call GlobalKey.currentState.method();
ParentWidget(){
GlobalKey<ChildWidgetState> globalKey = GlobalKey();
ChildWidget(key: globalKey);
...
globalKey.currentState.someMethod();
}
ChildWidget() {
ChildWidget({Key key}) : super(key: key);
someMethod(){
// some code, for example setState code
}
}
TestCode
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
GlobalKey<ChildWidgetState> globalKey = GlobalKey();
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: [
InkWell(
onTap: () {
globalKey.currentState.someMethod();
},
child: Text('ParentWidget'),
),
ChildWidget(key: globalKey),
],
);
}
}
class ChildWidget extends StatefulWidget {
ChildWidget({Key key}) : super(key: key);
#override
ChildWidgetState createState() => ChildWidgetState();
}
class ChildWidgetState extends State<ChildWidget> {
void someMethod() {
print('someMethod is called');
}
#override
Widget build(BuildContext context) {
return Text('childWidget');
}
}
edit: this approach works, but thanks #Andrija for pointing this out:
Just wanted to note - even though the answers below are good - you
should not be doing this in the first place. This is an anti-pattern
in flutter.

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.

How to implement Gesture detection - On Tap method in child class?

I want to implement the GestureDetector method onTapin child class. Is there a way to do it in Flutter ?
ParentClass.dart
Class ParentClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector {
onTap: methodA,
child: ChildClass(),
}
}
ChildClass.dart
Class ChildClass extends StatefulWidget {
methodA() // need to access methodA which is being passed to gesture detector
// How do I access methodA of parent class method here
// so whenever GestureDetector's onTap method is called, i want to handle that in ChildClass is there a way to do it ?
}
You can access the Child State methods using a unique key. Here is a minimal example:
Inside the ParentWidget, we define _childKey, a GlobalKey<_ChildWidgetState> that we then can use to access the State's method updateValue as follows:
_childKey.currentState.updateValue('Goodbye, Thierry!'),
Full source code
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: ParentWidget()),
);
}
}
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
final _childKey = GlobalKey<_ChildWidgetState>();
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => _childKey.currentState.updateValue('Goodbye, Thierry!'),
child: ChildWidget(key: _childKey),
);
}
}
class ChildWidget extends StatefulWidget {
const ChildWidget({Key key}) : super(key: key);
#override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
String value = 'Hello, Thierry!';
void updateValue(String newValue) {
setState(() => value = newValue);
}
#override
Widget build(BuildContext context) {
return Text(value);
}
}
In your ChildClass, return a GestureDetector. Set the child property to the rest of your widgets, and then set the onTap to call methodA. That should look something like this:
class ChildClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GestureDetector {
onTap: methodA,
child: SomeWidget(),
}
}
You are asking how to detect child class onTap and pass it to Parent right?
class YourChild extends StatelessWidget {
final Function parentCallback;
const YourChild({this.parentCallback});
#override
Widget build(BuildContext context) {
return GestureDetector(
// 1st option
onTap: () {
print("do something");
parentCallback();
},
)
}
}
Then for using It.
class YourParent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return YourChild( parentCallback(){
//do your stuff}
)
}
}

Flutter: Setting BuildContext to null from another class

I would like to know how to nullify a BuildContext from another class to prevent it from being used by an asynchronous function after context's widget has been disposed.
I assumed that it was passed by reference, so I tried setting it to null in the original widget (in my use case, I was setting it to null in the dispose method and the onWillPop method, both to no avail). I created a simple example app for what I would like to do. As you can see if you run the app, the context is not set to null.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Setting BuildContext to null',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Setting BuildContext to null'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
AsyncClass _asyncClass = AsyncClass();
Duration duration = const Duration(seconds: 5);
void toggleContext(BuildContext context){
Timer.periodic(duration, (_){
print("Setting context to null");
context = null;
});
}
#override
Widget build(BuildContext context) {
toggleContext(context);
_asyncClass.asyncFunc(context);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(),
);
}
}
class AsyncClass{
void asyncFunc(BuildContext context){
const duration = const Duration(seconds: 1);
Timer.periodic(duration, (_){
if(context==null){
print("Context is null");
} else {
print("Context is not null");
}
});
}
}
context in asyncFunc is simply a copy of the reference to the original BuildContext object on the Dart heap, so setting context to null will only set that particular variable to null, not all variables that have the same reference.
If you're from a C/C++ background, what you're doing is more akin to changing the address value of a pointer rather than changing the value that lives at the address a pointer points to.
EDIT: A potential solution is to do something like this:
class AsyncClass{
BuildContext context;
void asyncFunc(BuildContext param){
context = param;
const duration = const Duration(seconds: 1);
Timer.periodic(duration, (_){
if(context==null){
print("Context is null");
} else {
print("Context is not null");
}
});
}
}
and then do this:
void toggleContext(BuildContext context){
Timer.periodic(duration, (_){
print("Setting context to null");
_asyncClass.context = null;
});
}
I found that I can use the ValueParameter class to wrap the BuildContext object. This allows me to very simply pass the new ValueParameter value into functions and set them all to null very easily from any function it's incorporated in.
Here's my solution (I added if(_timer==null) to prevent duplicate Timers from forming when the application refreshes).
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Setting BuildContext to null',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Setting BuildContext to null'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
AsyncClass _asyncClass = AsyncClass();
Duration duration = const Duration(seconds: 5);
Timer _timer;
void toggleContext(ValueNotifier context) {
if (_timer == null)
_timer = Timer.periodic(duration, (_) {
print("Setting context to null");
context.value = null;
});
}
#override
Widget build(BuildContext param) {
ValueNotifier context = ValueNotifier(param);
toggleContext(context);
_asyncClass.asyncFunc(context);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(),
);
}
}
class AsyncClass {
Timer _timer;
void asyncFunc(ValueNotifier context) {
const duration = const Duration(seconds: 1);
if (_timer == null)
_timer = Timer.periodic(duration, (_) {
if (context.value == null) {
print("Context is null");
} else {
print("Context is not null");
}
});
}
}

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");
});
}