Как сделать прокручиваемый виджет в виде стека?
Я хотел бы создать компонент пользовательского интерфейса, который будет размещен поверх остальной части экрана, чтобы я мог перемещать его в представлении пользователя, но я также хочу сделать его прокручиваемым.
Экран без прокручиваемого компонента
Прокручиваемый компонент в верхней части экрана
Единственный способ, которым я мог придумать, - это использовать виджет 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