スクロールできる画面で、画面の半分までスクロールできるようにし、それより下には行けないようにする、というような処理を実装する方法について説明します。(需要があるかはわかりませんが)
実装自体はかなり簡単で、
- ScrollControllerを用意し、スクロール可能なウィジェットに設定する
- スクロールされた時に呼ばれる関数を用意し、一定のoffset(スクロール範囲の最上部からの距離を示す値のこと)を超えたらスクロール位置を限界値まで戻す処理を書く。
- initStateで上で用意した関数をScrollControllerのイベントとして追加する
といったところでしょうか。サンプルコードを用意しました。
なんの変哲もないStatefulWidgetです。
scrollLimitOffsetという変数に300を設定しています。つまり、スクロール範囲の最上部から300pxまでをスクロール範囲として設定しています。
_scrollListener()では、スクロールイベントが発火するたびに次に移動するoffsetを取得し、それが300を超えるようなら強制的に300地点に戻す、というやり方をしています。
class Page extends StatefulWidget {
const Page({Key? key}) : super(key: key);
@override
State<Page> createState() => _PageState();
}
class _PageState extends State<Page> {
//スクロールを制限する位置
final double scrollLimitOffset = 300;
//スクロールコントローラー
final ScrollController _controller = ScrollController();
@override
void initState() {
super.initState();
//スクロールコントローラーにイベントを設定
_controller.addListener(_scrollListener);
}
// スクロールを検知したときに呼ばれるイベント
void _scrollListener() {
print("スクロールを試みたオフセット${_controller.offset}");
if (_controller.offset > scrollLimitOffset) {
//スクロール範囲を強制的に戻す
_controller.jumpTo(scrollLimitOffset);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page'),
),
body: ListView.builder(
controller: _controller,
padding: const EdgeInsets.all(8),
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Center(child: Text(index.toString()));
}),
);
}
}
以下、全コードです。DartPadなどにコピペして動かしてみてください。
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Page(),
),
),
);
}
}
class Page extends StatefulWidget {
const Page({Key? key}) : super(key: key);
@override
State<Page> createState() => _PageState();
}
class _PageState extends State<Page> {
//スクロールを制限する位置
final double scrollLimitOffset = 300;
//スクロールコントローラー
final ScrollController _controller = ScrollController();
@override
void initState() {
super.initState();
//スクロールコントローラーにイベントを設定
_controller.addListener(_scrollListener);
}
// スクロールを検知したときに呼ばれるイベント
void _scrollListener() {
print("スクロールを試みたオフセット${_controller.offset}");
if (_controller.offset > scrollLimitOffset) {
//スクロール範囲を強制的に戻す
_controller.jumpTo(scrollLimitOffset);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page'),
),
body: ListView.builder(
controller: _controller,
padding: const EdgeInsets.all(8),
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Center(child: Text(index.toString()));
}),
);
}
}
コメント