Amjad Ali
Amjad Ali

Reputation: 21

Nested Scroll issue in DraggableScrollabeSheet

UI for dragsheet

What UI Have?

  1. DraggableScrollableSheet for bottom sheet with scroll behavior.
  2. A Top UI to achieving Sticky Top bar
  3. List of element to show the content

What should it do?

  1. Whole sheet should be draggable to Up and Down(Max and Min extend).
  2. on min extent, dragging up over sheet (Inc. Child scroll view) should drag up the whole sheet.
  3. on Max extent, If user is dragging down by holding top UI( Heading text + Drag handler) it should goes down.
  4. on Max extent, if User is grading on child scroll view first it should scroll all the content down and then the whole sheet.

Issue

  1. Wrapping with scrollview (SingleChildScrollView or ListView) make whole sheet scrollable but nested list won't have inherited scroll feature.
  2. Using CustomScrollView Have Inherited Scroll feature, but When sheet is on max extend and some content is scrolled up, scrolling down the whole sheet is not UX feasible (Friendly), since it will scroll the whole element first then the sheet, event on scrolling on TOP UI

DraggableScrollableSheet Code

DraggableScrollableSheet(
              controller: _sheetController,
              initialChildSize: 0.3,
              minChildSize: 0.3,
              maxChildSize: 0.9,
              builder: (context, scrollController) {
                return Container(
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius:
                        BorderRadius.vertical(top: Radius.circular(16)),
                  ),
                  child: Column(
                    children: [
                      // Sticky Header with Drag Handle
                      Container(
                        padding: EdgeInsets.all(16),
                        child: Column(
                          children: [
                            Container(
                              width: 40,
                              height: 4,
                              decoration: BoxDecoration(
                                color: Colors.grey,
                                borderRadius: BorderRadius.circular(2),
                              ),
                            ),
                            SizedBox(height: 8),
                            Text(
                              "Nearby Charging Stations",
                              style: TextStyle(
                                  fontSize: 18, fontWeight: FontWeight.bold),
                            ),
                          ],
                        ),
                      ),

                      // Scrollable List (Expanded to take available space)
                      Expanded(
                        child: ListView.builder(
                          controller:
                              scrollController, // Inherited scroll behavior
                          physics: BouncingScrollPhysics(),
                          itemCount: 20,
                          itemBuilder: (context, index) {
                            return ListTile(
                              leading: Icon(Icons.ev_station),
                              title: Text("Location name"),
                              subtitle: Text("Address details"),
                            );
                          },
                        ),
                      ),
                    ],
                  ),
                );
              },
            )

Full Screen Code

import 'package:flutter/material.dart';

class DraggableSheetExample extends StatefulWidget {
  @override
  _DraggableSheetExampleState createState() => _DraggableSheetExampleState();
}

class _DraggableSheetExampleState extends State<DraggableSheetExample> {
  final DraggableScrollableController _sheetController =
      DraggableScrollableController();
  final ScrollController _scrollController = ScrollController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // Main Content
          Positioned.fill(
            child: Container(
              color: Colors.blueGrey[100],
              child: Center(child: Text("Main Content")),
            ),
          ),

          // Draggable Sheet
          Align(
            alignment: Alignment.bottomCenter,
            child: DraggableScrollableSheet(
              controller: _sheetController,
              initialChildSize: 0.3,
              minChildSize: 0.3,
              maxChildSize: 0.9,
              builder: (context, scrollController) {
                return Container(
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius:
                        BorderRadius.vertical(top: Radius.circular(16)),
                  ),
                  child: Column(
                    children: [
                      // Sticky Header with Drag Handle
                      Container(
                        padding: EdgeInsets.all(16),
                        child: Column(
                          children: [
                            Container(
                              width: 40,
                              height: 4,
                              decoration: BoxDecoration(
                                color: Colors.grey,
                                borderRadius: BorderRadius.circular(2),
                              ),
                            ),
                            SizedBox(height: 8),
                            Text(
                              "Nearby Charging Stations",
                              style: TextStyle(
                                  fontSize: 18, fontWeight: FontWeight.bold),
                            ),
                          ],
                        ),
                      ),

                      // Scrollable List (Expanded to take available space)
                      Expanded(
                        child: ListView.builder(
                          controller:
                              scrollController, // Inherited scroll behavior
                          physics: BouncingScrollPhysics(),
                          itemCount: 20,
                          itemBuilder: (context, index) {
                            return ListTile(
                              leading: Icon(Icons.ev_station),
                              title: Text("Location name"),
                              subtitle: Text("Address details"),
                            );
                          },
                        ),
                      ),
                    ],
                  ),
                );
              },
            ),
          )
        ],
      ),
    );
  }
}

Upvotes: 2

Views: 43

Answers (1)

Akshat Bhuhagal
Akshat Bhuhagal

Reputation: 148

Scroll over top of the UI to achieve this, you can use a GestureDetector wrapped around the header section to detect drag gestures and programmatically control the sheet's size using the DraggableScrollableController. Meanwhile, the ListView.builder() will handle its own scrolling independently.

                  // Draggable Header Section
                  GestureDetector(
                    onVerticalDragUpdate: (details) {

                  // Adjust the sheet size based on drag gestures
                  double newSize = _sheetController.size - (details.primaryDelta! / MediaQuery.of(context).size.height);
                  _sheetController.jumpTo(newSize.clamp(0.3, 1.0));

                },
                child: Container(
                  width: MediaQuery.of(context).size.width,
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    children: [
                      Container(
                        width: 40,
                        height: 4,
                        decoration: BoxDecoration(
                          color: Colors.grey,
                          borderRadius: BorderRadius.circular(2),
                        ),
                      ),
                      const SizedBox(height: 8),
                      const Text(
                        "Nearby Charging Stations",
                        style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                      ),
                    ],
                  ),
                ),
              ),

Full Code :

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});


  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const DraggableSheetExample(),
    );
  }
}

class DraggableSheetExample extends StatefulWidget {
  const DraggableSheetExample({super.key});

  @override
  _DraggableSheetExampleState createState() => _DraggableSheetExampleState();
}

class _DraggableSheetExampleState extends State<DraggableSheetExample> {
  final DraggableScrollableController _sheetController = DraggableScrollableController();

  @override
  void dispose() {
    _sheetController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [

          // Main Content
          Positioned.fill(
            child: Container(
              color: Colors.blueGrey[100],
              child: const Center(child: Text("Main Content")),
            ),
          ),

          // Draggable Sheet
          Align(
            alignment: Alignment.bottomCenter,
            child: DraggableScrollableSheet(
              controller: _sheetController,
              initialChildSize: 0.3,
              minChildSize: 0.3,
              maxChildSize: 1,
              builder: (context, scrollController) {
                return Container(
                  decoration: const BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
                  ),
                  child: Column(
                    children: [

                      // Draggable Header Section
                      GestureDetector(
                        onVerticalDragUpdate: (details) {

                          // Adjust the sheet size based on drag gestures
                          double newSize = _sheetController.size - (details.primaryDelta! / MediaQuery.of(context).size.height);
                          _sheetController.jumpTo(newSize.clamp(0.3, 1.0));

                        },
                        child: Container(
                          width: MediaQuery.of(context).size.width,
                          padding: const EdgeInsets.all(16),
                          child: Column(
                            children: [
                              Container(
                                width: 40,
                                height: 4,
                                decoration: BoxDecoration(
                                  color: Colors.grey,
                                  borderRadius: BorderRadius.circular(2),
                                ),
                              ),
                              const SizedBox(height: 8),
                              const Text(
                                "Nearby Charging Stations",
                                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                              ),
                            ],
                          ),
                        ),
                      ),

                      // Scrollable List (Expanded to take available space)
                      Expanded(
                        child: ListView.builder(
                          controller: scrollController, // Inherited scroll behavior
                          physics: const ClampingScrollPhysics(),
                          itemCount: 20,
                          itemBuilder: (context, index) {
                            return const ListTile(
                              leading: Icon(Icons.ev_station),
                              title: Text("Location name"),
                              subtitle: Text("Address details"),
                            );
                          },
                        ),
                      ),

                    ],
                  ),
                );
              },
            ),
          )
        ],
      ),
    );
  }
}

Upvotes: 2

Related Questions