wonders/lib/ui/common/stacked_page_view_builder.dart

66 lines
2.1 KiB
Dart
Raw Permalink Normal View History

2022-08-29 20:38:28 -06:00
import 'package:wonders/common_libs.dart';
/// Decouples the scrollable area from the size of the PageView itself.
/// Helps simplify the layout of the underlying view and can create a better UX with screen-readers.
///
/// Internally it creates a stack with an invisible PageView on the bottom to handle scrolling,
/// then drives a second PageController (follower) which is passed to the `builder` so a nested PageView can
/// be created to hold the content itself.
///
/// Scrolling of the nested PageView will then be driven by the outer one.
///
class StackedPageViewBuilder extends StatefulWidget {
const StackedPageViewBuilder({
Key? key,
this.initialIndex = 0,
required this.pageCount,
required this.builder,
this.onInit,
}) : super(key: key);
final int initialIndex;
final int pageCount;
final Widget Function(BuildContext builder, PageController controller, PageController follower) builder;
final void Function(PageController controller, PageController follower)? onInit;
@override
State<StackedPageViewBuilder> createState() => _StackedPageViewBuilderState();
}
class _StackedPageViewBuilderState extends State<StackedPageViewBuilder> {
late final _controller = PageController(initialPage: widget.initialIndex);
late final _follower = PageController(initialPage: widget.initialIndex);
@override
void initState() {
super.initState();
_controller.addListener(_handleControllerChanged);
widget.onInit?.call(_controller, _follower);
}
@override
void dispose() {
_controller.dispose();
_follower.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned.fill(
child: ExcludeSemantics(
child: PageView.builder(
itemCount: widget.pageCount,
controller: _controller,
itemBuilder: (_, __) => Container(color: Colors.transparent),
),
),
),
widget.builder(context, _controller, _follower),
],
);
}
void _handleControllerChanged() => _follower.jumpTo(_controller.position.pixels);
}