-->

Flutter provider state management, logout concept

2020-03-26 07:37发布

问题:

I am trying to implement custom logout solution for my application, where no matter where user currently is, once the Logout button is clicked, app will navigate back to Login page.

My idea was, that instead of listening on every component for state changes, I would have one single listener on a master component -> MyApp.

For the sake of simplicity, I have stripped down items to bare minimum. Here is how my Profile class could look like:

class Profile with ChangeNotifier {
  bool _isAuthentificated = false;
  bool get isAuthentificated => _isAuthentificated;
  set isAuthentificated(bool newVal) {
    _isAuthentificated = newVal;
    notifyListeners();
  }
}

Now, under Main, I have registered this provider as following:

void main() => runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (_) => Profile(),
          )
        ],
        child: MyApp(),
      ),
    );

And finally MyApp component:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return Consumer<Profile>(
      builder: (context, profile, _) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            brightness: Brightness.light,
            primaryColor: Color.fromARGB(255, 0, 121, 107),
            accentColor: Color.fromARGB(255, 255, 87, 34),
          ),
          home: buildBasePage(context, profile),
        );
      },
    );
  }

  Widget buildBasePage(BuildContext context, Profile currentProfile) {
    return !currentProfile.isAuthentificated
        ? LoginComponent()
        : MyHomePage(title: 'Flutter Demo Home Page test');
  }
}

My idea was, that as MyApp component is the master, I should be able to create a consumer, which would be notified if current user is authentificated, and would respond accordingly.

What happens is, that when I am in e.g. MyHomePage component and I click Logout() method which looks like following:

  void _logout() {
    Provider.of<Profile>(context, listen: false).isAuthentificated = false;
  }

I would be expecting that upon changing property, the initial MyApp component would react and generate LoginPage; which is not the case. I have tried changing from Consumer to Provider.of<Profile>(context, listen: false) yet with the same result.

What do I need to do in order for this concept to work? Is it even correct to do it this way?

I mean I could surely update my Profile class in a way, that I add the following method:

  logout(BuildContext context) {
    _isAuthentificated = false;

    Navigator.push(
        context, MaterialPageRoute(builder: (context) => LoginComponent()));
  }

And then simply call Provider.of<Profile>(context, listen: false).logout(), however I thought that Provider package was designed for this...or am I missing something?

Any help in respect to this matter would be more than appreciated.

回答1:

I don't know why it wasn't working for you. Here is a complete example I built based on your description. It works!

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Profile with ChangeNotifier {
  bool _isAuthentificated = false;

  bool get isAuthentificated {
    return this._isAuthentificated;
  }

  set isAuthentificated(bool newVal) {
    this._isAuthentificated = newVal;
    this.notifyListeners();
  }
}

void main() {
  return runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<Profile>(
          create: (final BuildContext context) {
            return Profile();
          },
        )
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return Consumer<Profile>(
      builder: (final BuildContext context, final Profile profile, final Widget child) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(primarySwatch: Colors.blue),
          home: profile.isAuthentificated ? MyHomePage() : MyLoginPage(),
        );
      },
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Home [Auth Protected]")),
      body: Center(
        child: RaisedButton(
          child: const Text("Logout"),
          onPressed: () {
            final Profile profile = Provider.of<Profile>(context, listen: false);
            profile.isAuthentificated = false;
          },
        ),
      ),
    );
  }
}

class MyLoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Login")),
      body: Center(
        child: RaisedButton(
          child: const Text("Login"),
          onPressed: () {
            final Profile profile = Provider.of<Profile>(context, listen: false);
            profile.isAuthentificated = true;
          },
        ),
      ),
    );
  }
}


回答2:

You don't need to pass listen:false, instead simply call

Provider.of<Profile>(context).logout()

So your Profile class would look like

      class Profile with ChangeNotifier {
  bool isAuthentificated = false;


  logout() {
    isAuthentificated = false;
    notifyListeners();
  }
}