felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.75k stars 3.39k forks source link

Bloc getting value error #1266

Closed gerryau closed 4 years ago

gerryau commented 4 years ago

Describe the bug Error saying string value of a bloc state is empty, but the desired result is achieved on device when running.

Expected behavior No error.

════════ Exception caught by widgets library ═══════════════════════════════════ The following UnexpectedValueError was thrown building BlocBuilder<TodoTimerBlockFormBloc, TodoTimerBlockFormState>(dirty, state: _BlocBuilderBaseState<TodoTimerBlockFormBloc, TodoTimerBlockFormState>#f5386): "Encountered a ValueFailure at an unrecoverable point. Terminating. Failure was: ValueFailure.empty(failedValue: )"

The relevant error-causing widget was BlocBuilder<TodoTimerBlockFormBloc, TodoTimerBlockFormState> package:app/…/todo_timer_form/new_todo_timer_form_page.dart:69 When the exception was thrown, this was the stack

0 ValueObject.getOrCrash.

package:app/…/core/value_objects.dart:21

1 Left.fold

package:dartz/src/either.dart:124

2 ValueObject.getOrCrash

package:app/…/core/value_objects.dart:21

3 TimerFormScaffold.build.

package:app/…/todo_timer_form/new_todo_timer_form_page.dart:71

4 BlocBuilder.build

package:flutter_bloc/src/bloc_builder.dart:90 ... ════════════════════════════════════════════════════════════════════════════════

@injectable class TodoTimerBlockFormBloc extends Bloc<TodoTimerBlockFormEvent, TodoTimerBlockFormState> { final ITodoRepository _todoRepository;

TodoTimerBlockFormBloc(this._todoRepository);

@override TodoTimerBlockFormState get initialState => TodoTimerBlockFormState.initial();

@override Stream mapEventToState( TodoTimerBlockFormEvent event, ) async { yield event.map( initialized: (e) async* { yield e.initialTodoOption.fold( () => state, (initialTodo) { return state.copyWith( todo: initialTodo, ); }, ); }, ...

class NewTimerPage extends HookWidget { final TodoItem selectedTodo;

const NewTimerPage({ Key key, @required this.selectedTodo, }) : super(key: key);

@override Widget build(BuildContext context) { return BlocProvider( create: (context) => getIt() ..add(TodoTimerBlockFormEvent.initialized(optionOf(selectedTodo))), child: BlocConsumer<TodoTimerBlockFormBloc, TodoTimerBlockFormState>( listener: (context, state) { ... builder: (context, state) { return Stack( children: [ TimerFormScaffold(), ], ); } ), ); } }

class TimerFormScaffold extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: BlocBuilder<TodoTimerBlockFormBloc, TodoTimerBlockFormState>( builder: (context, state){ return Text(state.todo.name.getOrCrash()); }, ) ), ); } }

felangel commented 4 years ago

Hi @gerryau 👋 Thanks for opening an issue!

Can you please provide a link to a sample app which reproduces the issue? Thanks 🙏

gerryau commented 4 years ago

Hey @felangel thanks for your reply, I've just invited you to see the app, where the problem exists. It's under the todo-timer branch. Thanks

felangel commented 4 years ago

Hi @gerryau I am unable to run due to a missing GoogleService-Info.plist. Would it be possible for you to put together a simple sample app which ideally doesn't have a Firebase dependency and reproduces the issue? It would be much easier for me to help if I can easily reproduce the issue locally, thanks!

gerryau commented 4 years ago

No problem @felangel , see link: https://github.com/gerryau/todo-bloc

felangel commented 4 years ago

Hey @gerryau 👋

I took a look and the issue is there is a frame where the TodoTimerState is TodoTimerState.initial() which means that when you call state.todo.name.getOrCrash() it will crash since the todo is empty. This is because the bloc's initial state is TodoTimerState.initial() and when you add the TodoTimerEvent.initialized event it still takes a frame to propagate so the TimerFormScaffold will be rendered at least once where the state has not been updated.

I opened a pull request with one potential fix. Hope that helps!

Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation 👍

constantinLu commented 1 year ago

Hello, sorry for writing here, but I encountered this problem on my project as well and it's been a week and I could not fix the problem.

How did you managed to fix this issue ? . @gerryau

@felangel I understand the cause, but I tried in several ways to solve it and with no success. Can you help me with this issue ?

My project is: https://github.com/gizet/vault_pass

Class where the exception is thrown: AccountView. When I check my debug in the BlocObserver the the state changes correctly but when it reaches the BlocBuilder the initial value is taken into account and not the newly changed state with the initialized values.

Issue:

image

Stacktrace:
======== Exception caught by widgets library =======================================================
The following UnexpectedValueError was thrown building BlocBuilder<RecordBloc, RecordState>(dirty, dependencies: [_InheritedProviderScope<RecordBloc?>], state: _BlocBuilderBaseState<RecordBloc, RecordState>#77787):
"Encountered a Error at an unrecoverable point. Failure was: MicroTypeFailure<String>.invalidString(failedValue: )"

The relevant error-causing widget was: 
  BlocBuilder<RecordBloc, RecordState> BlocBuilder:file:///D:/_workspace/flutter/vault_pass/lib/presentation/view/records/account/account_view.dart:37:12
When the exception was thrown, this was the stack: 
#0      MicroType.get.<anonymous closure> (package:vault_pass/domain/microtypes/microtype.dart:15:30)
#1      Left.match (package:fpdart/src/either.dart:592:72)
#2      Either.fold (package:fpdart/src/either.dart:255:7)
#3      MicroType.get (package:vault_pass/domain/microtypes/microtype.dart:15:18)
#4      _AccountViewState.build.<anonymous closure> (package:vault_pass/presentation/view/records/account/account_view.dart:64:83)
#5      BlocBuilder.build (package:flutter_bloc/src/bloc_builder.dart:90:57)
#6      _BlocBuilderBaseState.build (package:flutter_bloc/src/bloc_builder.dart:166:21)
#7      StatefulElement.build (package:flutter/src/widgets/framework.dart:5080:27)
#8      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4968:15)
#9      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5133:11)
#10     Element.rebuild (package:flutter/src/widgets/framework.dart:4690:5)
#11     StatefulElement.update (package:flutter/src/widgets/framework.dart:5156:5)
#12     Element.updateChild (package:flutter/src/widgets/framework.dart:3660:15)
#13     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4993:16)
#14     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5133:11)
#15     Element.rebuild (package:flutter/src/widgets/framework.dart:4690:5)
#16     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2743:19)
#17     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:863:21)
#18     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:381:5)
#19     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1289:15)
#20     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1218:9)
#21     SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:942:7)
#25     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:26)
(elided 3 frames from class _Timer and dart:async-patch)
====================================================================================================
Reloaded 2 of 1556 libraries in 1,742ms (compile: 40 ms, reload: 338 ms, reassemble: 1243 ms).
D/EGL_emulation( 6445): app_time_stats: avg=53314.34ms min=53314.34ms max=53314.34ms count=1