1

I would like to create a UI component, which would be stacked on top of the rest of the screen, so I can transition it in the user's view, but I also want to make it scrollable.

Screen without the scrollable component

Scrollable component on top of the screen

The only way I could think of is to use Stack and Positioned widget, but unfortunately components within Stack can't be scrollable.

I'm thinking GestureDetector and scrolling the list to the right manually, which would mean placing large part of it outside of the screen's rendering area.

Can you think of a more elegant solution please?

Thanks, Tomas

1 Answers1

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

class SlidingDrawer extends StatelessWidget {
  final Widget drawer;
  final OpenableController openableController;

  SlidingDrawer({@required this.drawer, @required this.openableController});

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        GestureDetector(
          onTap: openableController.isOpen() ? openableController.close : null,
        ),
        FractionalTranslation(
          translation: Offset(1.0 - openableController.percentOpen, 0.0),
          child: Align(
            child: drawer,
            alignment: Alignment.centerRight,
          ),
        ),
      ],
    );
  }
}

class OpenableController extends ChangeNotifier {
  OpenedState _state;
  AnimationController openingController;

  OpenableController(
      {@required TickerProvider vsync, @required Duration openDuration})
      : openingController =
            AnimationController(vsync: vsync, duration: openDuration) {
    openingController
      ..addListener(notifyListeners)
      ..addStatusListener((status) {
        switch (status) {
          case AnimationStatus.forward:
            _state = OpenedState.opening;
            break;
          case AnimationStatus.reverse:
            _state = OpenedState.closing;
            break;
          case AnimationStatus.completed:
            _state = OpenedState.open;
            break;
          case AnimationStatus.dismissed:
            _state = OpenedState.closed;
            break;
        }

        notifyListeners();
      });
  }

  get state => _state;

  get percentOpen => openingController.value;

  bool isOpen() {
    return _state == OpenedState.open;
  }

  bool isOpening() {
    return _state == OpenedState.opening;
  }

  bool isClosed() {
    return _state == OpenedState.closed;
  }

  bool isClosing() {
    return _state == OpenedState.closing;
  }

  void open() {
    openingController.forward();
  }

  void close() {
    openingController.reverse();
  }

  void toggle() {
    if (isClosed()) {
      open();
    } else if (isOpen()) {
      close();
    }
  }
}

enum OpenedState { open, closed, opening, closing }

Above is a generic component, just provide content you want to be drawer from right, and put this in a stack,

in your main

 OpenableController openableController;


  @override
  void initState() {
    super.initState();
    openableController = OpenableController(
        vsync: this, openDuration: Duration(milliseconds: 250))
      ..addListener(() => setState(() {}));
  }

and to open

openableController.open
Lakhwinder Singh
  • 6,799
  • 4
  • 25
  • 42