Bloc: Question: Why the BlocBuilder is called without state change?

Created on 16 Nov 2019  路  2Comments  路  Source: felangel/bloc

Describe the bug
I find the BlocBuilder is called twice when App is navigated to another screen and back. I think the BlocBuilder shouldn't be called during the navigation because BlocState doesn't change during the navigation.

To Reproduce
Steps to reproduce the behavior:

  1. Click the "Go another page" button to another screen.
  2. Click the back button on the left top corner, back to the home screen.
  3. See the console log.

Expected behavior
I think the BlocBuilder shouldn't be called during the navigation.

*Logs *
Performing hot restart...
Restarted application in 1,325ms.
flutter: Out BlocBuilder
flutter: In BlocBuilder
flutter: In BlocBuilder
flutter: In BlocBuilder

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

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


void main() => runApp(BlocProvider<SomeBloc>(
      builder: (context) => SomeBloc(),
      child: MyApp(),
    ));

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      routes: {
        '/': (context) => HomePage(),
        '/another': (context) => AnotherPage(),
      },
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home Page')),
      body: Center(
        child: Column(
          children: [
            () {
              print('Out BlocBuilder');
              return Container();
            }(),
            BlocBuilder<SomeBloc, SomeState>(builder: (context, state) {
              print('In BlocBuilder');
              return Container();
            }),
            RaisedButton(
              child: Text('Go another page'),
              onPressed: () {
                Navigator.of(context).pushNamed('/another');
              },
            ),
          ],
        ),
      ),
    );
  }
}

class AnotherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Another Page')),
      body: Center(
        child: Text('Another Page'),
      ),
    );
  }
}

class SomeEvent extends Equatable {
  const SomeEvent();

  @override
  List<Object> get props => [];
}

class SomeState extends Equatable {
  const SomeState();

  @override
  List<Object> get props => [];
}

class SomeBloc extends Bloc<SomeEvent, SomeState> {
  @override
  SomeState get initialState => SomeState();

  @override
  Stream<SomeState> mapEventToState(
    SomeEvent event,
  ) async* {
    // TODO: Add Logic
  }
}
question

Most helpful comment

Thanks very much :)

All 2 comments

Hi @marklureal 馃憢
Thanks for opening an issue!

This is expected behavior and BlocBuilder can be built many times by Flutter itself. As a result, you need to make sure your builder is a pure function (no side-effects) and should assume it can be called 60 times per second (on every frame).

StreamBuilder behaves the same way:

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

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

void main() => runApp(BlocProvider<SomeBloc>(
      builder: (context) => SomeBloc(),
      child: MyApp(),
    ));

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      routes: {
        '/': (context) => HomePage(),
        '/another': (context) => AnotherPage(),
      },
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('HomePage build');
    return Scaffold(
      appBar: AppBar(title: Text('Home Page')),
      body: Center(
        child: Column(
          children: [
            StreamBuilder(
              initialData: null,
              stream: BlocProvider.of<SomeBloc>(context),
              builder: (context, state) {
                print('In StreamBuilder');
                return Container();
              },
            ),
            BlocBuilder<SomeBloc, SomeState>(
              builder: (context, state) {
                print('In BlocBuilder');
                return Container();
              },
            ),
            RaisedButton(
              child: Text('Go another page'),
              onPressed: () {
                Navigator.of(context).pushNamed('/another');
              },
            ),
          ],
        ),
      ),
    );
  }
}

class AnotherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Another Page')),
      body: Center(
        child: Text('Another Page'),
      ),
    );
  }
}

class SomeEvent extends Equatable {
  const SomeEvent();

  @override
  List<Object> get props => [];
}

class SomeState extends Equatable {
  const SomeState();

  @override
  List<Object> get props => [];
}

class SomeBloc extends Bloc<SomeEvent, SomeState> {
  @override
  SomeState get initialState => SomeState();

  @override
  Stream<SomeState> mapEventToState(
    SomeEvent event,
  ) async* {
    // TODO: Add Logic
  }
}

Hope that helps 馃憤

Thanks very much :)

Was this page helpful?
0 / 5 - 0 ratings