Search code examples
flutterdrag-and-dropdraggable

Flutter Draggable widget is not moving when dragging inside Stack > AnimatedPositioned > GestureDetector > Listview


I'm currently developing scratch-like block-coding application with flutter.

What I want to achieve is to make keyword-blocks listed at PaletteBlocks

Below is what I've implemented so far

// absolute_align.dart

class AbsoluteAlign extends StatelessWidget {
  const AbsoluteAlign({
    Key? key,
    Alignment? alignment,
    required this.child,
  })  : alignment = alignment ?? Alignment.topLeft,
        super(key: key);

  final Alignment alignment;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    AbsoluteAlignPosition position = AbsoluteAlignPosition(
      context: context,
      alignment: alignment,
      horizontalMovement: 0,
      verticalMovement: 0,
      isActive: false,
    );
    return Positioned(
      child: child,
      top: position.top,
      left: position.left,
      bottom: position.bottom,
      right: position.right,
    );
  }
}

class AnimatedAbsoluteAlign extends StatelessWidget {
  AnimatedAbsoluteAlign({
    Key? key,
    Alignment? alignment,
    required this.isActive,
    required this.child,
    required this.horizontalMovement,
    required this.verticalMovement,
    required this.duration,
  })  : alignment = alignment ?? Alignment.topLeft,
        super(key: key);

  final Alignment alignment;
  final Widget child;
  final bool isActive;
  final double horizontalMovement;
  final double verticalMovement;
  final Duration duration;

  @override
  Widget build(BuildContext context) {
    AbsoluteAlignPosition position = AbsoluteAlignPosition(
      context: context,
      alignment: alignment,
      horizontalMovement: horizontalMovement,
      verticalMovement: verticalMovement,
      isActive: isActive,
    );
    return AnimatedPositioned(
      duration: duration,
      child: child,
      top: position.top,
      left: position.left,
      bottom: position.bottom,
      right: position.right,
      curve: Curves.ease,
    );
  }
}

class AbsoluteAlignPosition {
  // skipped for brevity
}

// studio.dart

/// Widget structure:
/// [SingleChildScrollView] >> [Stack] >> [List<AbsoluteAlign>]
class Studio extends StatelessWidget {
  Studio({
    Key? key,
    required this.width,
    required this.height,
  }) : super(key: key);
  final ScrollController _scrollController = ScrollController();
  final double width;
  final double height;

  @override
  Widget build(BuildContext context) {
    final studioBloc = context.read<StudioBloc>();
    final paletteBloc = context.watch<PaletteBloc>();
    final vm = studioBloc.state.vm;
    return SingleChildScrollView(
      controller: _scrollController,
      physics: const BouncingScrollPhysics(
        parent: AlwaysScrollableScrollPhysics(),
      ),
      child: Container(
        width: width,
        height: height,
        child: Stack(
          fit: StackFit.loose,
          children: [
            AbsoluteAlign(child: Background()),
            AbsoluteAlign(child: Editor()),
            AbsoluteAlign(
              child: StudioTitle(
                title: vm.runtime.current.name,
              ),
            ),
            AbsoluteAlign(
              alignment: Alignment.bottomCenter,
              child: Toolbar(),
            ),
            AnimatedAbsoluteAlign(
              alignment: Alignment.centerRight,
              verticalMovement: 0,
              horizontalMovement: TangibleConstant.paletteWidth - 30,
              isActive: paletteBloc.state.status == PaletteStatus.closed,
              duration: const Duration(milliseconds: 200),
              child: Palette(),
            ),
          ],
        ),
      ),
    );
  }
}

// palette.dart

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

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => _onTap(context),
      onHorizontalDragUpdate: (details) =>
          _onHorizontalDragUpdate(context, details),
      child: Container(
        width: TangibleConstant.paletteWidth,
        height: TangibleConstant.paletteHeight,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.all(Radius.circular(10)),
          color: TangibleColors.studioTeal,
        ),
        child: PaletteBlocks(),
      ),
    );
  }

  // skipped for brevity
}

class PaletteBlocks extends StatelessWidget {
  PaletteBlocks({Key? key}) : super(key: key);
  final ScrollController _scrollController = ScrollController();

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
      controller: _scrollController,
      physics: const BouncingScrollPhysics(
        parent: AlwaysScrollableScrollPhysics(),
      ),
      children: _buildPaletteBlocks(context),
    );
  }

  List<Widget> _buildPaletteBlocks(BuildContext context) {
    final studioBloc = context.read<StudioBloc>();
    final vm = studioBloc.state.vm;
    return vm.registeredBlocks
        .map((block) => PaletteBlock(block: block))
        .fold<List<Widget>>(
            <Widget>[],
            (previousValue, element) => [
                  ...previousValue,
                  SizedBox(
                    height: 5,
                  ),
                  element
                ]).toList();
  }
}

class PaletteBlock extends StatelessWidget {
  PaletteBlock({required this.block});

  final Block block;

  @override
  Widget build(BuildContext context) {
    return Draggable(
      data: block,
      feedback: SizedBox(height: TangibleConstant.paletteBlockHeight),
      child: Container(
        width: TangibleConstant.paletteBlockWidth,
        height: TangibleConstant.paletteBlockHeight,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.all(Radius.circular(10)),
          color: TangibleColors.blockPurple,
          border: Border.all(color: Colors.deepPurple),
        ),
        child: Center(
          child: Text(
            block.opcode,
            style: TangibleTheme.standard.textTheme.headline4,
          ),
        ),
      ),
    );
  }
}

But when I drag that keyword-block, it does not move, although the dragging action seems to be detected by the widget since onDragStarted method from Draggable fires when I drag the block. What should I do to fix it?


Solution

  • My dummy mistake. I mistakely understood what feedback and child does in Draggable widget. feedback is the widget attatched to the pointer (a.k.a your finger). child acts like placeholder in this case.