Home > Back-end >  How to convert setState to BLoC?
How to convert setState to BLoC?

Time:05-12

In my application, I use the Bottom Navy Bar to display tabs with screens at the bottom of the screen. To switch between tabs I use setState. Now I need to redo all the setState that I have in block. I cannot understand how this can be done. Help solve this issue. Attached below is my screen code.

void main() {
  BlocOverrides.runZoned(
    () {
      runApp(const MyApp());
    },
    blocObserver: MainBlocObserver(),
  );
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
        ...
        child: PageView(
          controller: _pageController,
          onPageChanged: _onPageChanged,
          children: [
            SearchScreen(),
            EventListScreen(),
            Container(color: Colors.green),
            Container(color: Colors.blue),
          ],
        ),
      ),
      bottomNavigationBar: BottomNavyBar(
        containerHeight: 60,
        selectedIndex: _currentIndex,
        onItemSelected: _onItemSelected,
        items: [
         ...
        ],
      ),
    );
  }

  //change page
  void _onPageChanged(index) {
    setState(() => _currentIndex = index);
  }

  //select page item
  void _onItemSelected(index) {
    setState(() => _currentIndex = index);
    _pageController.jumpToPage(index);
  }
}

class MainBlocObserver extends BlocObserver {

}

CodePudding user response:

In fact you can do it in Cubit which is the same as Bloc and a little simpler, but I did it in Bloc with events, I hope my explanation can help you:

First of all you create your respective files to Bloc, in my case I called them in the following way :

enter image description here

BottomNavyBloc :

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

part 'bottom_navy_event.dart';
part 'bottom_navy_state.dart';

class BottomNavyBloc extends Bloc<BottomNavyEvent, BottomNavyState> {
  BottomNavyBloc() : super(const BottomNavyState()) {
    on<ChangePageEvent>(
      (event, emit) => emit(state.copyWith(index: event.index)),
    );
  }
}

This would be the body of your Bloc, therefore it only waits for the call of the ChangePageEvent, that will is used to receive and to send the main index of your navigation and to be able to change it in your UI.

BottomNavyEvent :

part of 'bottom_navy_bloc.dart';

abstract class BottomNavyEvent extends Equatable {
  const BottomNavyEvent();

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

class ChangePageEvent extends BottomNavyEvent {
  final int index; // declaration

  const ChangePageEvent({
    required this.index, // we recive the index of the navigation
  });

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

This way you create your ChangePageEvent event class, notice that we also use the Equatable package that serves us to compare the states in a simpler way.

BottomNavyState :

part of 'bottom_navy_bloc.dart';

class BottomNavyState extends Equatable {
  final int index;

  const BottomNavyState({
    this.index = 0,
  });

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

  BottomNavyState copyWith({
    int? index,
  }) {
    return BottomNavyState(
      index: index ?? this.index,
    );
  }
}

There exists another form to make call to your states, nevertheless this I find it more useful and simple for the fact of having all the variables to the hand when we want to occupy them.

Notice that in comparison to the events, the state will serve to obtain that index that you send, in other words, the UI is the one that listens the states that change (in this case the index), but so that it changes a state it is necessary to send a value by means of an event

main :

void main() {
  BlocOverrides.runZoned(
    () {
      runApp(const MyApp());
    },
    blocObserver: MainBlocObserver(),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => BottomNavyBloc(), // first you have to provide it and create it in your MaterialApp 
      child: MaterialApp(
        title: 'Booking',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const HomeScreen(),
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final PageController controller = PageController();

    return Scaffold(
      body: BlocListener<BottomNavyBloc, BottomNavyState>( // use it to listen if the state changes in this case the index
        listener: (context, state) {
          controller.jumpToPage(state.index);
        },
        child: PageView(
          controller: controller,
          onPageChanged: (int index) {
            BlocProvider.of<BottomNavyBloc>(context).add(ChangePageEvent(
              index: index,
            )); // calling the event and sending the current index
          },
          children: [
            Container(color: Colors.green),
            Container(color: Colors.blue),
            Container(color: Colors.red),
            Container(color: Colors.yellow),
          ],
        ),
      ),
      bottomNavigationBar: BlocBuilder<BottomNavyBloc, BottomNavyState>( // Builder is for change or re-build the UI, Listener listens only the things "intern" or for not re-building
        builder: (context, state) {
          return BottomNavyBar(
            containerHeight: 60,
            selectedIndex: state.index, // getting the current index from the state
            onItemSelected: (int index) {
              BlocProvider.of<BottomNavyBloc>(context).add(ChangePageEvent(
                index: index,
              )); // calling again the event
            },
            items: [
              BottomNavyBarItem(
                icon: const Icon(Icons.apps),
                title: const Text('Home'),
                activeColor: Colors.red,
              ),
              BottomNavyBarItem(
                  icon: const Icon(Icons.people),
                  title: const Text('Users'),
                  activeColor: Colors.purpleAccent),
              BottomNavyBarItem(
                  icon: const Icon(Icons.message),
                  title: const Text('Messages'),
                  activeColor: Colors.pink),
              BottomNavyBarItem(
                  icon: const Icon(Icons.settings),
                  title: const Text('Settings'),
                  activeColor: Colors.blue),
            ],
          );
        },
      ),
    );
  }
}

class MainBlocObserver extends BlocObserver {}

----------------------- CUBIT

It is the same as Bloc only it is simpler since you do not call any event, I recommend using Cubit if you are for this type of small logic, I leave it up to you.

enter image description here

BottomNavyCubit :

import 'package:bloc/bloc.dart';

part 'bottom_navy_state.dart';

class BottomNavyCubit extends Cubit<BottomNavyState> {
  BottomNavyCubit() : super(const BottomNavyState(index: 0));

  void changePage(int newIndex) => emit(BottomNavyState(index: newIndex)); // we don't use an event here, just a function
}

BottomNavyState :

part of 'bottom_navy_cubit.dart';

class BottomNavyState {
  final int index;

  const BottomNavyState({required this.index});
}

main :

void main() {
  BlocOverrides.runZoned(
    () {
      runApp(const MyApp());
    },
    blocObserver: MainBlocObserver(),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => BottomNavyCubit(), // creation of the cubit
      child: MaterialApp(
        title: 'Booking',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const HomeScreen(),
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final PageController controller = PageController();

    final BottomNavyCubit cubit = BlocProvider.of<BottomNavyCubit>(context); // instance of the cubit to call the function

    return Scaffold(
      body: BlocListener<BottomNavyCubit, BottomNavyState>(
        listener: (context, state) {
          controller.jumpToPage(state.index); // listening the state changin
        },
        child: PageView(
          controller: controller,
          onPageChanged: (int index) {
            cubit.changePage(index); // calls the function
          },
          children: [
            Container(color: Colors.green),
            Container(color: Colors.blue),
            Container(color: Colors.red),
            Container(color: Colors.yellow),
          ],
        ),
      ),
      bottomNavigationBar: BlocBuilder<BottomNavyCubit, BottomNavyState>(
        builder: (context, state) {
          return BottomNavyBar(
            containerHeight: 60,
            selectedIndex: state.index, // getting the state
            onItemSelected: (int index) {
              cubit.changePage(index); // calling the function
            },
            items: [
              BottomNavyBarItem(
                icon: const Icon(Icons.apps),
                title: const Text('Home'),
                activeColor: Colors.red,
              ),
              BottomNavyBarItem(
                  icon: const Icon(Icons.people),
                  title: const Text('Users'),
                  activeColor: Colors.purpleAccent),
              BottomNavyBarItem(
                  icon: const Icon(Icons.message),
                  title: const Text('Messages'),
                  activeColor: Colors.pink),
              BottomNavyBarItem(
                  icon: const Icon(Icons.settings),
                  title: const Text('Settings'),
                  activeColor: Colors.blue),
            ],
          );
        },
      ),
    );
  }
}
  • Related