How to update DragTarget when the state of the Draggable child change?

Viewed 506

I have a list that contains a number of Draggables and DragTargets widgets. I want to change the color of the DragTargets when the state of a Draggable widget changes.

  1. Initially, the color of all DragTargets widgets should be grey but they should change to green when a Draggable widget is pressed.
  2. The color of all DragTargets widgets should change to grey again when the drag operation is completed or canceled.

I'm trying to achieve it by using flutter_bloc but I'm getting the following error: Error: Could not find the correct Provider<MyBloc> above this _InheritedProviderScope<MyBloc> Widget.

Here is a minimal example:

  • ListItemBloc(MyBLoc)
class ListItemBloc extends Bloc<ListItemEvent, ListItemState> {
  ListItemBloc() : super(ListItemInitial());

  @override
  Stream<ListItemState> mapEventToState(
    ListItemEvent event,
  ) async* {
    if (event is OnDragStarted) {
      yield ListItemUpdated(true, false, false);
    } else if (event is OnDragCompleted) {
      yield ListItemUpdated(false, true, false);
    } else if (event is OnDraggableCanceled) {
      yield ListItemUpdated(false, false, true);
    }
  }
}
  • ListItemState
abstract class ListItemState extends Equatable {
  @override
  List<Object?> get props => [];
}

class ListItemInitial extends ListItemState {}

class ListItemUpdated extends ListItemState {
  final bool isOnDragStarted;
  final bool isOnDragCompleted;
  final bool isOnDraggableCanceled;

  ListItemUpdated(
    this.isOnDragStarted,
    this.isOnDragCompleted,
    this.isOnDraggableCanceled,
  );

  @override
  List<Object?> get props => [
        isOnDragStarted,
        isOnDragCompleted,
        isOnDraggableCanceled,
      ];
}
  • ListItemEvent
abstract class ListItemEvent extends Equatable {
  @override
  List<Object?> get props => [];
}

class OnDragStarted extends ListItemEvent {}

class OnDragCompleted extends ListItemEvent {}

class OnDraggableCanceled extends ListItemEvent {}
  • Visual
...

List<String> folders = ['folderA', 'folderB'];

List<String> images = [
  'https://images.unsplash.com/photo-1622495805731-eefd22e836f4?ixid=MnwxMjA3fDF8MHxlZGl0b3JpYWwtZmVlZHwxfHx8ZW58MHx8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60',
  'https://images.unsplash.com/photo-1622472263439-28f6e98d05b7?ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw0fHx8ZW58MHx8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60',
  'https://images.unsplash.com/photo-1622497274862-2dd4cd0ddaac?ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw1fHx8ZW58MHx8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60',
  'https://images.unsplash.com/photo-1622506614848-65988f1f6b33?ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyfHx8ZW58MHx8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60',
];

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  ...

  @override
  Widget build(BuildContext context) {
    final _length = folders.length + images.length;

    return BlocProvider<ListItemBloc>(
      create: (context) => context.read<ListItemBloc>(),
      child: MaterialApp(
        ...
        home: ListView.builder(
          itemCount: _length,
          itemBuilder: (context, index) {
            if (index < folders.length) {
              return MyDragTargetCard(folderName: folders[index]);
            }

            return MyDraggableCard(
              index: index - 2,
              imageUrl: images[index - 2],
            );
          },
        ),
      ),
    );
  }
}
class MyDraggableCard extends StatelessWidget {
  final int index;
  final String imageUrl;

  ...

  @override
  Widget build(BuildContext context) {
    print(context.read<ListItemBloc>().state); // error while trying to know the current state
    final GlobalKey _draggableKey = GlobalKey();

    Image _image = Image.network(imageUrl);

    return Draggable<Image>(
      dragAnchorStrategy: pointerDragAnchorStrategy,
      data: Image.network(imageUrl),
      feedback: DraggingCard(
        key: Key('_draggingCard$index'),
        dragKey: _draggableKey,
        image: _image,
      ),
      child: Card(
        ...
        child: _image,
      ),
      onDragStarted: () => print('onDragStarted'),
      onDragCompleted: () => print('onDragCompleted'),
      onDraggableCanceled: (Velocity velocity, Offset offset) {
        print('onDraggableCanceled');
      },
    );
  }
}
class DraggingCard extends StatelessWidget {
  final GlobalKey dragKey;
  final Image image;

  ...

  @override
  Widget build(BuildContext context) {
    final GlobalKey _draggableKey = GlobalKey();

    return FractionalTranslation(
      translation: const Offset(-0.5, -0.5),
      child: ClipRRect(
        key: _draggableKey,
        ...
        child: SizedBox(
          height: 150,
          width: 150,
          child: Opacity(
            opacity: 0.85,
            child: image,
          ),
        ),
      ),
    );
  }
}
class _MyDragTargetCardState extends State<MyDragTargetCard> {
  late Color _folderColor;

  @override
  void initState() {
    _folderColor = Colors.grey;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    print(context.read<ListItemBloc>().state); // error while trying to know the current state
    return DragTarget<Image>(
      builder: (context, candidateData, rejectedData) {
        return Card(
          color: _folderColor,
          child: Center(child: Text(widget.folderName)),
        );
      },
      onWillAccept: (item) => true,
      onAccept: (item) => print('onAccept'),
      onLeave: (item) => setState(() {
        _folderColor = Colors.green;
      }),
      onMove: (item) => setState(() {
        _folderColor = Colors.red;
      }),
    );
  }
}

Any suggestions on how to get the current state of the bloc without getting the mentioned error?

0 Answers
Related