Bloc Library Null

В homepage в appbar отображаю иконки авторизованного и не авторизованного юсера. Если войти, иконка профиля отображается , но выводит ошибку A build function returned null. Если нажать на профиль, инфо отображается но снова выводит эту же ошибку. Вернувшись назад вместо иконок красный блок с ошибкой. Почему возвращает null? main:

class TestApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (context) => AuthBloc(
              authService: AuthService())
            ..add(
              AppStart(),
            ),
        ),
      ],
      child: MaterialApp(
        home: HomePage(),
      ),
    );
  }
}

homepage:

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: <Widget>[
          BlocBuilder<AuthBloc, AuthState>( 
            builder: (context, state) {
              if (state is Authenticated) {
                user = state.user;
              } else if (state is UnAuthenticated) {
                return logIn(context);
              }
              else {
               return user == null ? Container() : profileIcon(context); // !!!
              }
            },
          ),
        ],
      ),
    );
  }
}

bloc

@override
Stream<AuthState> mapEventToState(AuthEvent event) async* {
  if (event is AppStart) {
    try {
      final user = await AuthService.getCurrentUser();
      yield Authenticated(user: user);
    } catch (e) {
      yield UnAuthenticated();
    }
  }
}

profileIcon:

Widget profileIcon(context) {
  return Row(
    children: <Widget>[
      FlatButton.icon(
        icon: Icon(
        label: Text(
          'Профиль',
        ),
        onPressed: () async {
          Navigator.push(context,
              MaterialPageRoute(builder: (context) => UserProfile()));
        },
      ),
    ],
  );
}

Ответы (1 шт):

Автор решения: MiT

Если вы используете builder (а по сути это обычный метод build) вы обязаны возвращать виджет.

У вас до этого была конструкция такая:

builder: (context, state) {
  if (state is Authenticated) {
    return profileIcon(context);
  } else if (state is UnAuthenticated) {
    return logIn(context);
  }
},

То есть, вы не оброботали все возможные варианты выхода из этого builder. После вы изменили его на такой:

builder: (context, state) {
  if (state is Authenticated) {
    return profileIcon(context);
  } else if (state is UnAuthenticated) {
    return logIn(context);
  }
  else {
    return Container()
  }
},

Здесь вы добавили выход из builder.


А теперь почему BLoC, не срабатывает. Приходящий state не является Authenticated или UnAuthenticated, а нечто другим, это вы сам и показали когда добавили проверку с Container().

И второе замечание, вы не используете приходящий state, не извлекаете из него данные, это не event чтобы распознать откуда пришло событие, это состояние обработанного event (то есть, оно должно перестраивать ui основываясь на данных из state).


Такая конструкция проверки:

if (...) {...}
else if (...) {...}

Эквивалентно такой:

if (...) {...}
if (...) {...}

Как вы можете понять, не всегда можно попасть в эти две проверки...


Как должно быть по идее.

homepage:

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: <Widget>[
          BlocBuilder<AuthBloc, AuthState>( 
            builder: (context, state) {
              if (state is Authenticated) {
                profileIcon(context, state); // Передаем состояние
              } else if (state is UnAuthenticated) {
                return logIn(context);
              }
              else {
               // Возможно мы не попадаем в проверки, по этому узнаем какой тип к нам пришел.
               print(state.runtimeType);
               // И показываем заглушку в виде контейнера
               return Container();
              }
            },
          ),
        ],
      ),
    );
  }
}

profileIcon:

Widget profileIcon(context, state) {
  return Row(
    children: <Widget>[
      FlatButton.icon(
        icon: Icon(...),
        label: Text('Профиль'),
        onPressed: () async {
          // Также передаем user в UserProfile, ведь это данные о User...
          Navigator.push(context,
              MaterialPageRoute(builder: (context) => UserProfile(state.user)));
        },
      ),
    ],
  );
}
→ Ссылка