0

I created a class LoginStore extending ChangeNotifier to state manage my application and i'm using Provider for dependency injection. According to the documentation, invoking context.watch() is enough to make the Widget listen for changes in the LoginStore and then rebuild, but something is wrong and this is not working.

This is my view:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:trade_magic/src/login/login_store.dart';

class TestView extends StatelessWidget {
  const TestView({super.key});

  @override
  Widget build(BuildContext context) {
    final store = context.watch<LoginStore>();
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Row(
            children: [
              Expanded(
                  child: TextField(
                onChanged: (value) => store.value.username = value,
              ))
            ],
          ),
          Expanded(child: Text(store.value.username))
        ],
      ),
    );
  }
}

This is my store:

class LoginStore extends ValueNotifier<Login> {
  LoginStore(super.value);

  LoginStore.empty() : this(Login(username: "", password: ""));
}

This is my model:

import 'dart:convert';

class Login {
  String username;

  String password;

  //set username(username) => this.username = username;

  Login({
    required this.username,
    required this.password,
  });

  Login copyWith({
    String? username,
    String? password,
  }) {
    return Login(
      username: username ?? this.username,
      password: password ?? this.password,
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'username': username,
      'password': password,
    };
  }

  factory Login.fromMap(Map<String, dynamic> map) {
    return Login(
      username: map['username'] as String,
      password: map['password'] as String,
    );
  }

  String toJson() => json.encode(toMap());

  factory Login.fromJson(String source) =>
      Login.fromMap(json.decode(source) as Map<String, dynamic>);

  @override
  String toString() => 'Login(username: $username, password: $password)';

  @override
  bool operator ==(covariant Login other) {

    return other.username == username && other.password == password;
  }

  @override
  int get hashCode => username.hashCode ^ password.hashCode;
}

This is App.dart:

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:trade_magic/src/login/login_store.dart';
//import 'package:trade_magic/src/login/login_view.dart';

import 'login/test_view.dart';
import 'sample_feature/sample_item_details_view.dart';
import 'sample_feature/sample_item_list_view.dart';
import 'settings/settings_controller.dart';
import 'settings/settings_view.dart';

class MyApp extends StatelessWidget {
  const MyApp({
    super.key,
    required this.settingsController,
  });

  final SettingsController settingsController;

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: settingsController,
      builder: (BuildContext context, Widget? child) {
        return MultiProvider(
          providers: [
            ChangeNotifierProvider<LoginStore>(
                create: (_) => LoginStore.empty())
          ],
          child: MaterialApp(
            restorationScopeId: 'app',

            localizationsDelegates: const [
              AppLocalizations.delegate,
              GlobalMaterialLocalizations.delegate,
              GlobalWidgetsLocalizations.delegate,
              GlobalCupertinoLocalizations.delegate,
            ],
            supportedLocales: const [
              Locale('en', ''), // English, no country code
              Locale('pt', 'BR')
            ],

            onGenerateTitle: (BuildContext context) =>
                AppLocalizations.of(context)!.appTitle,

            theme: ThemeData(),
            darkTheme: ThemeData.dark(),
            themeMode: settingsController.themeMode,

            onGenerateRoute: (RouteSettings routeSettings) {
              return MaterialPageRoute<void>(
                settings: routeSettings,
                builder: (BuildContext context) {
                  switch (routeSettings.name) {
                    case SettingsView.routeName:
                      return SettingsView(controller: settingsController);
                    case SampleItemDetailsView.routeName:
                      return const SampleItemDetailsView();
                    case SampleItemListView.routeName:
                    default:
                      return const TestView();
                  }
                },
              );
            },
          ),
        );
      },
    );
  }
}

Most questions in stackoverflow deal with Future, but this is not the case. I checked if the value of LoginStore was really changing, and it was. I expected TestView to rebuild and update the value in the Text widget, but that not happened. Why? What am I doing wrong?

Paulo Fernando
  • 3,148
  • 3
  • 5
  • 21
João Soares
  • 43
  • 1
  • 8

0 Answers0