Created
October 28, 2020 03:36
-
-
Save tranductam2802/9a9f5b41bf0d5d4bd011f28a33026d6f to your computer and use it in GitHub Desktop.
Demo infinite scroll controller
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter/scheduler.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Tutorial by Tran Duc Tam', | |
home: ProcessPage(), | |
); | |
} | |
} | |
const colorsList = [ | |
Colors.blue, | |
Colors.amber, | |
Colors.indigo, | |
]; | |
class ProcessPage extends StatefulWidget { | |
const ProcessPage(); | |
@override | |
_ProcessPageState createState() => _ProcessPageState(); | |
} | |
class _ProcessPageState extends State<ProcessPage> { | |
@override | |
Widget build(BuildContext context) { | |
final pageController = CirclePageController(itemCounts: colorsList.length); | |
return Scaffold( | |
appBar: AppBar(title: Text('')), | |
body: Center( | |
child: Container( | |
width: 400, | |
height: 400, | |
padding: EdgeInsets.all(2), | |
color: Colors.red, | |
child: PageView.builder( | |
controller: pageController, | |
itemCount: null, | |
physics: PageScrollPhysics(), | |
itemBuilder: (context, index) { | |
final localIndex = pageController.toLocalIndex(index); | |
return Container( | |
color: colorsList[localIndex], | |
child: Center( | |
child: Text('$localIndex'), | |
), | |
); | |
}, | |
), | |
), | |
), | |
); | |
} | |
} | |
/// 円環を制御PageController | |
class CirclePageController extends PageController { | |
/// コンストラクタ | |
CirclePageController({ | |
@required this.itemCounts, | |
bool keepPage = true, | |
double viewportFraction = 1.0, | |
int initialPage, | |
}) : assert(itemCounts != null), | |
assert(itemCounts >= 0), | |
assert(keepPage != null), | |
assert(viewportFraction != null), | |
assert(viewportFraction > 0.0), | |
super( | |
initialPage: itemCounts < kEnableCircleMinItemCounts | |
? initialPage % itemCounts | |
: (itemCounts + 1) % itemCounts, | |
keepPage: keepPage, | |
viewportFraction: viewportFraction, | |
); | |
/// 円環機能が有効か | |
bool get isEnableCircle => itemCounts >= kEnableCircleMinItemCounts; | |
/// データ一覧のインデックス | |
int get localIndex => localPage.truncate(); | |
/// データ一覧のスクロール位置 | |
double get localPage => isEnableCircle ? toLocalPage(page) : page; | |
/// ページビューのインデックスからデータ一覧のインデックスに変換 | |
int toLocalIndex(int index) { | |
if (isEnableCircle) { | |
return (index - initialPage) % itemCounts; | |
} else { | |
return index; | |
} | |
} | |
/// データ一覧のインデックスからのページビューのインデックスに変換 | |
int toGlobalIndex(int index) { | |
if (isEnableCircle) { | |
return index + initialPage; | |
} else { | |
return index; | |
} | |
} | |
/// ページビューのスクロール位置からデータ一覧のスクロール位置に変換 | |
double toLocalPage(double page) { | |
if (isEnableCircle) { | |
final iPage = page.truncate(); | |
final decimal = page - iPage; | |
return toLocalIndex(iPage) + decimal; | |
} else { | |
return page; | |
} | |
} | |
/// ページビューのスクロール位置を変更する時、処理行う | |
void _circleScrollListener() { | |
if (page - initialPage < 0.1 - 1) { | |
// ページビューの一番左のページになったら | |
SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |
super.jumpToPage(itemCounts); | |
}); | |
} else if (page - initialPage - itemCounts > -0.1) { | |
// ページビューの一番右のページになったら | |
SchedulerBinding.instance.addPostFrameCallback((timeStamp) { | |
super.jumpToPage(initialPage); | |
}); | |
} | |
} | |
@override | |
void attach(ScrollPosition position) { | |
super.attach(position); | |
// ページビューのスクロール位置を変更イベント登記 | |
if (isEnableCircle) { | |
position.addListener(_circleScrollListener); | |
} | |
} | |
@override | |
void detach(ScrollPosition position) { | |
super.detach(position); | |
// ページビューのスクロール位置を変更イベント解除 | |
position.removeListener(_circleScrollListener); | |
} | |
@override | |
void dispose() { | |
// ページビューのスクロール位置を変更イベント解除 | |
for (final position in positions) { | |
position.removeListener(_circleScrollListener); | |
} | |
super.dispose(); | |
} | |
// データ数 | |
final int itemCounts; | |
// データ数は2項目未満場合、円環制御をしないような定数 | |
static const int kEnableCircleMinItemCounts = 2; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment