Reputation: 5968
I have created an Homepage and from that user can sign in for the app and in the next screen user can see their profile info(Only profile name) and under that their is signOut button. User can signOut from the app using signOut button.But it's not working for me.
I want to call signOut method from main.dart by pressing signOut button in details.dart(both the classes are in different file)
But when i press signOut Button in details.dart nothing happens!
And code is given below:
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'details.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
MyHomePageState createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
static bool _LoginButton = true;
void signOut(){
googleSignIn.signOut();
setState((){
_LoginButton = true;
});
print(_LoginButton);
print("User Signed Out");
}
Future<FirebaseUser> _signIn() async{
if(_LoginButton==true){
setState((){
_LoginButton=false;
});
GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
FirebaseUser firebaseUser = await firebaseAuth.signInWithGoogle(idToken: googleSignInAuthentication.idToken, accessToken: googleSignInAuthentication.accessToken);
print("Username is "+firebaseUser.displayName);
setState((){
_LoginButton = true;
});
Navigator.push(context, MaterialPageRoute(builder: (context) => details(firebaseUser.displayName,signOut)));
return firebaseUser;
}
}
bool _LoginButtonBool(){
return _LoginButton;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Google auth with firebase"),),
body: Center(
child: _LoginButtonBool()?Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
MaterialButton(onPressed: _LoginButtonBool() ? () => _signIn().then((FirebaseUser firebaseuser ) =>print(firebaseuser)).catchError((e) => print(e)): null,
child: Text("Login"),color: Colors.orange,),
],
),
):CircularProgressIndicator(backgroundColor: Colors.greenAccent.withOpacity(0.01),),
),
);
}
}
details.dart
import 'package:flutter/material.dart';
import 'package:flutter_auth/main.dart';
class details extends StatelessWidget {
String name;
final Function callback;
details(this.name,this.callback);
@override
Widget build(BuildContext context) {
return Scaffold(
body:Center(child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text(name),
MaterialButton(onPressed: () => callback,
child: Text("Log out"),color: Colors.orange),
],
),),
);
}
}
Upvotes: 30
Views: 151358
Reputation: 27990
A global key given to a StatefulWidget
can give us access to its methods from anywhere.
GlobalKey
exampleWidgetA
below has a method login()
.
To access login()
from other widgets, instantiate WidgetA
with a GlobalKey
.
Then use that key to access WidgetA
state to call login()
.
The structure below is:
ExamplePage
WidgetA
LoginDialog
LoginDialog
will call WidgetA.login()
using the global key.
login()
will update the AppBar
in WidgetA
with the user name.
WidgetA
Here is the StatefulWidget
WidgetA
:
class WidgetA extends StatefulWidget {
const WidgetA({Key? key}) : super(key: key);
@override
State<WidgetA> createState() => WidgetAState();
static GlobalKey<WidgetAState> createKey() => GlobalKey<WidgetAState>();
}
class WidgetAState extends State<WidgetA> {
String loginStatus = 'Signed Out';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$loginStatus'),
),
body: Center(
child: ElevatedButton(
child: Text('Login'),
onPressed: () =>
showDialog(context: context,
builder: (context) => LoginDialog()),
),
),
);
}
void login(String msg) {
setState(() {
loginStatus = msg;
});
}
}
Notes
createKey()
is a convenience method to create the type of global key we need, a GlobalKey
with a Type of WidgetAState
WidgetA
, we'll need to give it a key of type GlobalKey<WidgetAState>
GlobalKey<WidgetAState>
Here's the global key we'll create of type WidgetAState
, using the convenience static method createKey()
we added to WidgetA
class.
final widgetA = WidgetA.createKey();
To make accessing WidgetAState
cleaner, we can create an optional extension class on GlobalKey<WidgetAState>
types that gives us direct access to WidgetAState
:
extension WidgetAKeyExt on GlobalKey<WidgetAState> {
void login(String user) => currentState?.login(user);
}
This allows us to make this call:
widgetA.login()
instead of this:
widgetA.currentState?.login()
LoginDialog
This login dialog will access WidgetA's
login()
method using the global key.
class LoginDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Login'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
child: Text('Login'),
onPressed: () => widgetA.login('Billy'), // WidgetA method call
),
ElevatedButton(
child: Text('Logout'),
onPressed: () => widgetA.login('Signed Out'), // WidgetA method call
),
],
),
);
}
}
Here's the entire code sample in a single block to copy/paste. Also includes another page WidgetB
which also accesses WidgetA
state through the global key.
When a login is perfomed in the LoginDialog
, the app bar in WidgetA
will reflect the loginStatus
state. That state is also accessible in WidgetB
using the global key.
import 'package:flutter/material.dart';
/// Share access to a widget's methods & state by using a GlobalKey.
/// Create a GlobalKey<T> using that widget's state class as T.
/// For example below we use GlobalKey<WidgetAState>.
///
/// Give that key to WidgetA's constructor / instantiation.
///
/// Then use that key from anywhere to call methods on WidgetAState.
/// Create the key for [WidgetAState] globally so it can be accessed across your app.
/// i.e. do this outside of a Widget, such as in your main.dart or in a state
/// management solution that you access globally, etc.
///
/// For readability/ease we've created a static method [createKey] on [WidgetA] to
/// instantiate the global key we need.
///
/// This could also just be `final widgetA = GlobalKey<WidgetAState>();
final widgetA = WidgetA.createKey(); // see static method inside WidgetA
/// Just an empty page/route to hold [WidgetA] instantiation with global key.
class ExampleGlobalKeyPage extends StatelessWidget {
const ExampleGlobalKeyPage();
@override
Widget build(BuildContext context) {
return WidgetA(key: widgetA);
}
}
/// [WidgetA] takes a key argument. We'll give it the global key we created so
/// we can access its state object from anywhere.
class WidgetA extends StatefulWidget {
const WidgetA({Key? key}) : super(key: key);
@override
State<WidgetA> createState() => WidgetAState();
/// Convenience static method to create the [WidgetAState] key to provide to [WidgetA].
/// By being specific with the [Key] class as "GlobalKey<WidgetAState>" instead
/// of just "Key", we can use create & use an extension class [WidgetAKeyExt]
/// to make accessing the widget state cleaner & easier. See that extension
/// class below.
static GlobalKey<WidgetAState> createKey() => GlobalKey<WidgetAState>();
}
/// This is [WidgetA]'s state object. By default it's private, but we'll
/// make it public by removing '_' from the name, so [_WidgetAState]
/// becomes [WidgetAState].
///
/// Its [login] method will be available anywhere using the global key we gave
/// [WidgetA] constructor.
///
class WidgetAState extends State<WidgetA> {
String loginStatus = 'Signed Out'; // this state can be changed with the GlobalKey
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$loginStatus'),
actions: [
IconButton(icon: Icon(Icons.arrow_circle_right_rounded), onPressed: gotoB,)
],
),
body: Center(
child: ElevatedButton(
child: Text('Login'),
onPressed: () => showDialog(context: context,
builder: (context) => LoginDialog()),
),
),
);
}
/// This method can be called from [LoginDialog] widget using the GlobalKey
void login(String msg) {
setState(() {
loginStatus = msg;
});
}
void gotoB() => Navigator.push(context, MaterialPageRoute(builder: (c) => WidgetB()));
}
/// This extension class is not needed, but makes access to the [login] method
/// a little cleaner.
///
/// This extension method on GlobalKey<WidgetAState> performs the null check
/// for [currentState] existence before calling [login],
/// wherever/whenever we use [widgetA] key.
///
/// Also it uses "currentState" for us.
/// So our call `widgetA.currentState?.login()` is now `widgetA.login()`.
///
extension WidgetAKeyExt on GlobalKey<WidgetAState> {
void login(String user) => currentState?.login(user);
String get loginStatus => currentState?.loginStatus ?? 'Signed Out';
}
/// This Dialog is a separate route & widget. It makes calls to [WidgetA] methods
/// using the global key [widgetA].
class LoginDialog extends StatelessWidget {
/// For visibility you may want to pass the global key as a constructor arg
/// rather than using it directly. If so, you'd do something like:
//final GlobalKey<WidgetAState> widgetA;
//const LoginDialog({super.key, required this.widgetA});
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Login'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
child: Text('Login'),
onPressed: () => widgetA.login('Billy'), // WidgetA method call
),
ElevatedButton(
child: Text('Logout'),
onPressed: () => widgetA.login('Signed Out'), // WidgetA method call
),
],
),
);
}
}
class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${widgetA.loginStatus}'),
),
body: Center(
child: Text('Some other page accessing WidgetA'),
),
);
}
}
Upvotes: -1
Reputation: 1222
It is simple let me explain with an example
class Animals
{
var animalList = ['dog','cat','cow'];
// function for printing the list of animals
void animalListPrinter(){
for(var animal in animalList){
print(animal);
}
}
}
Calling the above function to another class
class ShowingAnimalList extends StatelessWidget {
final Animals ani= new Animals();
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap:()=> ani.animalListPrinter(),
);
}
}
You can call any Widget with this from the parent class
Upvotes: 27
Reputation: 569
We can access it easily just like below.
className().MethodName(),
Upvotes: 11
Reputation: 35
you can create another logout() function and give context of home to push back to sign in screen/home screen , works for me as :
logout() async {
await googleSignIn.signOut();
Navigator.push(context, MaterialPageRoute(builder: (context) => Home()));
}
Upvotes: 2
Reputation: 20548
You must be careful with what you are trying to do because you might be accessing a page/widget that is not mounted. Imagine you do a pushReplacement(new MaterialPageroute(...))
. The previous page is no longer available in the tree so you can't access it nor any of its methods.
Unless you have a clear parent child relationship in your tree, you should abstract away your logic to external or business logic classes. Thus you are sure that you are calling active instances of your classes.
Here is an example of what you could use passing around the Business object. It would be even better if you use other patterns like BLOC, ScopedModel, Streams, etc. But for the sake of simplicity I think this should be enough.
import "package:flutter/material.dart";
void main() {
runApp(MyApp(new Logic()));
}
class Logic {
void doSomething() {
print("doing something");
}
}
class MyApp extends StatelessWidget {
final Logic logic;
MyApp(this.logic);
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new HomePage(widget.logic),
);
}
}
class HomePage extends StatelessWidget {
final Logic logic;
HomePage(this.logic);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: () { Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => AnotherPage(logic),
))},
child: Text("Go to AnotherPage"),
),
),
);
}
}
class AnotherPage extends StatelessWidget {
final Logic logic;
AnotherPage(this.logic);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: logic.doSomething,
child: Text("Press me"),
),
),
);
}
}
If you still want to call a function in the other Page and you are sure the page is mounted (you have done a push
instead of a pushReplacement
) you could do the following. (handle with care)
class HomePage extends StatelessWidget {
HomePage();
void onCalledFromOutside() {
print("Call from outside");
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: () { Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => AnotherPage(onCalledFromOutside),
))},
child: Text("Go to AnotherPage"),
),
),
);
}
}
class AnotherPage extends StatelessWidget {
final Function callback
AnotherPage(this.callback);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
onPressed: callback,
child: Text("Press me"),
),
),
);
}
}
Upvotes: 17
Reputation: 19
Import HomePage class in DetailsPage and make a new instance out of it, then call the method you want if it's a public one.
Upvotes: 0