Flutter BlocBuilder builder вызывается только изначально и не реагирует на изменение состояния

#flutter #dart #flutter-bloc

Вопрос:

Я только недавно начал заниматься Флаттером. Блок действительно имеет крутую кривую обучения…

Как видно из названия, логика BlocBuilder правильно выполняется только один раз, при запуске приложения. Однако впоследствии пользовательский интерфейс не перестраивается при изменении состояния. Хотя все события излучаются, и состояния меняются.

Заранее спасибо за любую помощь!

Вот мой «главный дротик».:

 import 'package:co_flutter/auth/authentication_bloc.dart';
import 'package:co_flutter/auth/authentication_event.dart';
import 'package:co_flutter/auth/authentication_state.dart';
import 'package:co_flutter/auth/login/login_bloc.dart';
import 'package:co_flutter/auth/signup/signup_page.dart';
import 'package:co_flutter/loading_indicator.dart';
import 'package:co_flutter/splash_page.dart';
import 'package:co_flutter/user_repository.dart';

import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'auth/login/login_page.dart';

class SimpleBlocObserver extends BlocObserver {
  @override
  void onCreate(BlocBase bloc) {
    super.onCreate(bloc);
    print('onCreate -- ${bloc.runtimeType}');
  }

  @override
  void onEvent(Bloc bloc, Object? event) {
    super.onEvent(bloc, event);
    print('onEvent -- ${bloc.runtimeType}, $event');
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print('onTransition -- ${bloc.runtimeType}, $transition');
  }

  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('onChange -- ${bloc.runtimeType}, $change');
  }

  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    print('onError -- ${bloc.runtimeType}, $error');
    super.onError(bloc, error, stackTrace);
  }

  @override
  void onClose(BlocBase bloc) {
    super.onClose(bloc);
    print('onClose -- ${bloc.runtimeType}');
  }
}

void main() {
  Bloc.observer = SimpleBlocObserver();
  runApp(MyApp(
    userRepository: UserRepository(),
  ));
}

class MyApp extends StatefulWidget {
  final UserRepository userRepository;

  MyApp({Key? key, required this.userRepository}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late AuthenticationBloc authenticationBloc;
  UserRepository get userRepository => widget.userRepository;

  @override
  void initState() {
    authenticationBloc = AuthenticationBloc(userRepository: userRepository);
    authenticationBloc.add(AppStarted());
    super.initState();
  }

  @override
  void dispose() {
    authenticationBloc.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<AuthenticationBloc>(
          create: (BuildContext context) => authenticationBloc,
        ),
        BlocProvider<LoginBloc>(
          create: (BuildContext context) => LoginBloc(
              userRepository: userRepository,
              authenticationBloc: authenticationBloc),
        ),
      ],
      child: MaterialApp(
        title: 'My App',
        theme: ThemeData(
          primarySwatch: Colors.indigo,
        ),
        home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
          builder: (BuildContext context, AuthenticationState state) {
            if (state is AuthenticationUninitialized) {
              return SplashPage();
            }
            if (state is AuthenticationAuthenticated) {
              return Dashboard(
                title: 'Dashboard',
              );
            }
            if (state is AuthenticationUnauthenticated) {
              return LoginPage(
                userRepository: userRepository,
              );
            }
            if (state is AuthenticationLoading) {
              return LoadingIndicator();
            }
            // else {
            // return Text('Error');
            // }
            return BlocBuilder<LoginBloc, LoginState>(
              builder: (context, state) {
                if (state is LoginToSignup) {
                  return SignUpPage();
                } else
                  return SizedBox.shrink();
              },
            );
          },
        ),
      ),
    );
  }
}

class Dashboard extends StatelessWidget {
  final String title;

  const Dashboard({Key? key, required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final AuthenticationBloc authenticationBloc =
        BlocProvider.of<AuthenticationBloc>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Dashboard'),
      ),
      body: Container(
        child: Center(
          child: ElevatedButton(
            child: Text('logout'),
            onPressed: () {
              authenticationBloc.add(LoggedOut());
            },
          ),
        ),
      ),
    );
  }
}

 

аутентификация_блок

 import 'dart:async';

import 'package:co_flutter/auth/authentication_event.dart';
import 'package:co_flutter/auth/authentication_state.dart';
import 'package:bloc/bloc.dart';
import '../user_repository.dart';

class AuthenticationBloc
    extends Bloc<AuthenticationEvent, AuthenticationState> {
  UserRepository userRepository;

  AuthenticationBloc({required this.userRepository})
      : super(AuthenticationUninitialized()) {
    userRepository = UserRepository();
  }

  @override
  Stream<AuthenticationState> mapEventToState(
    AuthenticationEvent event,
  ) async* {
    if (event is AppStarted) {
      final bool hasToken = await userRepository.hasToken();

      if (hasToken) {
        yield AuthenticationAuthenticated();
      } else {
        yield AuthenticationUnauthenticated();
      }
    }

    if (event is LoggedIn) {
      yield AuthenticationLoading();
      await userRepository.persistToken(event.token, event.userId);
      yield AuthenticationAuthenticated();
    }

    if (event is LoggedOut) {
      yield AuthenticationLoading();
      await userRepository.deleteToken();
      yield AuthenticationUnauthenticated();
    }
  }
}

 

authentication_event

 import 'package:equatable/equatable.dart';

abstract class AuthenticationEvent extends Equatable {
  @override
  List<Object> get props => [];
}

class AppStarted extends AuthenticationEvent {
  @override
  String toString() => 'AppStarted';
}

class LoggedIn extends AuthenticationEvent {
  final String token;
  final String userId;

  LoggedIn({required this.token, required this.userId});

  @override
  String toString() => 'LoggedIn { token: $token}';
}

class LoggedOut extends AuthenticationEvent {
  @override
  String toString() => 'LoggedOut';
}

 

состояние аутентификации

 import 'package:equatable/equatable.dart';

abstract class AuthenticationState extends Equatable {
  const AuthenticationState();
}

class AuthenticationUninitialized extends AuthenticationState {
  @override
  List<Object> get props => [];
}

class AuthenticationLoading extends AuthenticationState {
  @override
  List<Object> get props => [];
}

class AuthenticationAuthenticated extends AuthenticationState {
  @override
  List<Object> get props => [];
}

class AuthenticationUnauthenticated extends AuthenticationState {
  @override
  List<Object> get props => [];
}

 

Комментарии:

1. Можете ли вы попробовать настроить bloc параметр в своем BlocBuilder authenticationBloc «кому»?

2. Который BlocBuilder не перестраивает <AuthenticationBloc, AuthenticationState> или <LoginBloc, LoginState>

3. @Stijn2210 это не помогает, я уже пробовал это раньше.

4. @croxx5f на самом деле они оба.

Ответ №1:

Проблема вот в чем. Вы уже создали блок:

 authenticationBloc = AuthenticationBloc(userRepository: userRepository);
 

И вот вы пытаетесь создать его снова.

Чтобы исправить это, замените этот код:

 BlocProvider<AuthenticationBloc>(
                  create: (BuildContext context) => authenticationBloc,
                ),
 

с этим:

 BlocProvider<AuthenticationBloc>.value(
          value: authenticationBloc,
        ),
 

Передайте блок в конструктор блоков

 BlocBuilder<AuthenticationBloc, AuthenticationState>(
 bloc: authenticationBloc,
 

Не забудьте удалить блок authenticationBloc после того, как вы использовали его в функции dispose.