Как сделать прокручиваемый виджет в виде стека?

Я хотел бы создать компонент пользовательского интерфейса, который будет размещен поверх остальной части экрана, чтобы я мог перемещать его в представлении пользователя, но я также хочу сделать его прокручиваемым.

Экран без прокручиваемого компонента

Прокручиваемый компонент в верхней части экрана

Единственный способ, которым я мог придумать, - это использовать виджет Stack и Positioned, но, к сожалению, компоненты в Stack не могут быть прокручиваемыми.

Я думаю о GestureDetector и прокручиваю список вправо вручную, что будет означать размещение большей его части за пределами области рендеринга экрана.

Можете ли вы придумать более элегантное решение, пожалуйста?

Спасибо, Томас

1 ответ

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 }

Выше приведен общий компонент, просто предоставьте контент, который вы хотите, чтобы он был справа, и поместите его в стек,

в твоем главном

 OpenableController openableController;


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

и открыть

openableController.open
Другие вопросы по тегам