Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter DraggableScrollableSheet - how to expand/collapse programmatically

I'm wondering if anyone knows/found a way to make Flutters' DraggableScrollableSheet expand/collapse programmatically. I'm using Flutters latest build from their Dev channel which allows me to wrap it in a

NotificationListener<DraggableScrollableNotification>

which I can then listen to the extend of how far the sheets is expanded / collapsed. However, I'm not clear on how I would be able to collapse an expanded sheet or vice versa.

Seems in the widget src file there's a

DraggableScrollableActuator

that exposes a static .reset but I don't know/or think of a way to make that work.

like image 429
Andre Avatar asked Jun 29 '19 20:06

Andre


2 Answers

Building on Pierre's answer I ended up implementing a workaround that allows me to use DraggableScrollableActuator to both collapse and expand the DraggableScrollableSheet.

You can use the setState method to change the value of initialChildSize and then use the DraggableScrollableActuator.reset method to either expand or collapse the sheet.

void toggleDraggableScrollableSheet() {
  if (draggableSheetContext != null) {
    setState(() {
      initialExtent = isExpanded ? minExtent : maxExtent;
    });
    DraggableScrollableActuator.reset(draggableSheetContext);
  }
}

Important thing to make this work is to provide a different Key for when the widget is collapsed and expanded. This will result in 2 instances of _DraggableScrollableSheetState - one that you will be able to reset to collapsed state and another that you will be able to reset to expanded state.

DraggableScrollableActuator(
  child: DraggableScrollableSheet(
    key: Key(initialExtent.toString()),
    minChildSize: minExtent,
    maxChildSize: maxExtent,
    initialChildSize: initialExtent,
    builder: draggableScrollableSheetBuilder,
  ),
)

Edit:

Working example:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  static const List<Color> colors = [
    Colors.red,
    Colors.green,
    Colors.blue,
  ];

  static const double minExtent = 0.2;
  static const double maxExtent = 0.6;

  bool isExpanded = false;
  double initialExtent = minExtent;
  BuildContext draggableSheetContext;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: _buildBody(),
      ),
    );
  }

  Widget _buildBody() {
    return InkWell(
      onTap: _toggleDraggableScrollableSheet,
      child: DraggableScrollableActuator(
        child: DraggableScrollableSheet(
          key: Key(initialExtent.toString()),
          minChildSize: minExtent,
          maxChildSize: maxExtent,
          initialChildSize: initialExtent,
          builder: _draggableScrollableSheetBuilder,
        ),
      ),
    );
  }

  void _toggleDraggableScrollableSheet() {
    if (draggableSheetContext != null) {
      setState(() {
        initialExtent = isExpanded ? minExtent : maxExtent;
        isExpanded = !isExpanded;
      });
      DraggableScrollableActuator.reset(draggableSheetContext);
    }
  }

  Widget _draggableScrollableSheetBuilder(
    BuildContext context,
    ScrollController scrollController,
  ) {
    draggableSheetContext = context;
    return SingleChildScrollView(
      controller: scrollController,
      child: Column(
        children: colors
            .map((color) => Container(
                  height: 200,
                  width: double.infinity,
                  color: color,
                ))
            .toList(),
      ),
    );
  }
}
like image 57
gtrochimiuk Avatar answered Sep 17 '22 10:09

gtrochimiuk


You can reset the position of the DraggableScrollableSheet to its initialChildSizeby using the DraggableScrollableActuator. Therefore you need to have the BuildContext, which is given to you by the DraggableScrollableSheet builder-parameter.

For example, you can create a Button inside the DraggableScrollableSheetand call DraggableScrollableActuator.reset(context);

DraggableScrollableSheet(
  builder: (BuildContext context, ScrollController scrollController) {

    return MaterialButton(
      onPressed: () {
        DraggableScrollableActuator.reset(context);
      },
    );

  },
)

If you want to reset the DraggableScrollableSheet from outside its build-function, you need to create a property of BuildContext, which can be used to save the DraggableScrollableSheet context into.

like image 26
Pierre Fischer Avatar answered Sep 19 '22 10:09

Pierre Fischer