Home > database >  How to use BlocBuilder inside ReorderableListView?
How to use BlocBuilder inside ReorderableListView?

Time:06-04

When I try to use BlocBuilder inside ReorderableListView i get an Could not find the correct Provider<ThemeCubit> above this BlocBuilder<ThemeCubit, ThemeState> Widget error. The error occurs when I drag an item inside list.

Example:

List<int> _items = List<int>.generate(50, (int index) => index);

ReorderableListView(
      children: <Widget>[
        for (int index = 0; index < _items.length; index  = 1)
          Container(
            key: Key(index.toString()),
            child: BlocBuilder<ThemeCubit, ThemeState>(
              builder: (___, ____) {
                return Text('Item ${_items[index]}');
              }
            )
          )
      ],
      onReorder: (int oldIndex, int newIndex) {},
    )

Weirdly enough, if I try to access the bloc data with context.read<ThemeCubit>() I don't get an error.


Reproducible example (main.dart)

Problem is reproduced when you drag an item in list.

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

class SimpleCubit extends Cubit<int>
{
  SimpleCubit() : super(0);
}

void main() async {
  runApp(MyTrip());
}

class AppName extends StatelessWidget {
  AppName({super.key});

  final List<int> _items = List<int>.generate(50, (int index) => index);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: MultiBlocProvider(
          providers: [
            BlocProvider<SimpleCubit>(
              create: (_) => SimpleCubit()
            ),
          ],
          child: ReorderableListView(
            children: <Widget>[
              for (int index = 0; index < _items.length; index  = 1)
                Container(
                  key: Key(index.toString()),
                  child: BlocBuilder<SimpleCubit, int>(
                    builder: (_, state) {
                      return Text(state.toString());
                    }
                  )
                )
            ],
            onReorder: (int oldIndex, int newIndex) {},
          ),
        ),
      )
    );
  }
}

CodePudding user response:

Just wrap your MaterialApp with the BlocProvider, your error indicates that it does not find the context, since you only provide it to the ReorderableListView widget, so you better create your bloc globally so that any widget with context can call it that way: (BUT IT HAS TO BE IN YOUR MAIN MATERIAL APP in MyTrip())

void main() async {
  runApp(MyTrip());
}

class MyTrip extends StatelessWidget {

 MyTrip({super.key});
    
 @override
  Widget build(BuildContext context) {
    return MultiBlocProvider( //  like this
      providers: [
        BlocProvider<SimpleCubit>(create: (_) => SimpleCubit()),
      ],
      child: MaterialApp(
        [...] // here it has to be your routes, theme, translation, etc...
      ),
    );
  }

CodePudding user response:

I could not reproduce the issue with the example provided above. But I can assume what your problem is.

I modified your Reproducible example to see the same error you get:

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

class SimpleCubit extends Cubit<int> {
  SimpleCubit() : super(0);

  void onPress() {
    print('press');
  }
}

void main() async {
  runApp(MyTrip());
}

class MyTrip extends StatelessWidget {
  MyTrip({super.key});

  final List<int> _items = List<int>.generate(50, (int index) => index);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: MultiBlocProvider(
          providers: [
            BlocProvider<SimpleCubit>(create: (_) => SimpleCubit()),
          ],
          child: ReorderableListView(
            children: <Widget>[
              for (int index = 0; index < _items.length; index  = 1)
                Container(
                  key: Key(index.toString()),
                  child: BlocBuilder<SimpleCubit, int>(
                    builder: (_, state) {
                      return GestureDetector(
                        onTap: context.read<SimpleCubit>().onPress,
                        child: Text(state.toString()),
                      );
                    },
                  ),
                ),
            ],
            onReorder: (int oldIndex, int newIndex) {},
          ),
        ),
      ),
    );
  }
}

The explanation of this error is that you cannot call bloc methods if you provided this bloc inside the same build method, because you're referring to the context that does not contain such bloc:

enter image description here

Please see the modified version of your example that should work:

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

class SimpleCubit extends Cubit<int> {
  SimpleCubit() : super(0);

  void onPress() {
    print('press');
  }
}

void main() async {
  runApp(MyTrip());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: MultiBlocProvider(
          providers: [
            BlocProvider<SimpleCubit>(create: (_) => SimpleCubit()),
          ],
          child: _MyTrip(),
        ),
      ),
    );
  }
}

class _MyTrip extends StatelessWidget {
  _MyTrip({super.key});

  final List<int> _items = List<int>.generate(50, (int index) => index);

  @override
  Widget build(BuildContext context) {
    return ReorderableListView(
      children: <Widget>[
        for (int index = 0; index < _items.length; index  = 1)
          Container(
            key: Key(index.toString()),
            child: BlocBuilder<SimpleCubit, int>(
              builder: (_, state) {
                return GestureDetector(
                  onTap: context.read<SimpleCubit>().onPress,
                  child: Text(state.toString()),
                );
              },
            ),
          ),
      ],
      onReorder: (int oldIndex, int newIndex) {},
    );
  }
}

Here we separated bloc provisioning and usage. That's how it's designed to use.

  • Related