Skip to content

Commit 19be5cd

Browse files
committed
update: example
1 parent c67adae commit 19be5cd

16 files changed

+350
-23
lines changed

example/lib/features/scene/anchor_demo/anchor_page.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class _AnchorListPageState extends State<AnchorListPage>
2020
late ListObserverController observerController;
2121
late TabController _tabController;
2222
List tabs = ["News(0)", "History(5)", "Picture(10)"];
23-
List<int> tabIndexs = [0, 5, 10];
23+
List<int> tabIndexes = [0, 5, 10];
2424

2525
@override
2626
void initState() {
@@ -48,7 +48,7 @@ class _AnchorListPageState extends State<AnchorListPage>
4848
tabs: tabs.map((e) => Tab(text: e)).toList(),
4949
onTap: (index) {
5050
observerController.animateTo(
51-
index: tabIndexs[index],
51+
index: tabIndexes[index],
5252
duration: const Duration(milliseconds: 250),
5353
curve: Curves.ease,
5454
);
@@ -62,7 +62,7 @@ class _AnchorListPageState extends State<AnchorListPage>
6262
onObserve: (resultModel) {
6363
_tabController.index = ObserverUtils.calcAnchorTabIndex(
6464
observeModel: resultModel,
65-
tabIndexs: tabIndexs,
65+
tabIndexes: tabIndexes,
6666
currentTabIndex: _tabController.index,
6767
);
6868
},

example/lib/features/scene/anchor_demo/anchor_waterfall_page.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class _AnchorWaterfallPageState extends State<AnchorWaterfallPage>
2222
late GridObserverController observerController;
2323
late TabController _tabController;
2424
List tabs = ["News(0)", "History(5)", "Picture(10)"];
25-
List<int> tabIndexs = [0, 5, 10];
25+
List<int> tabIndexes = [0, 5, 10];
2626

2727
@override
2828
void initState() {
@@ -50,7 +50,7 @@ class _AnchorWaterfallPageState extends State<AnchorWaterfallPage>
5050
tabs: tabs.map((e) => Tab(text: e)).toList(),
5151
onTap: (index) {
5252
observerController.animateTo(
53-
index: tabIndexs[index],
53+
index: tabIndexes[index],
5454
duration: const Duration(milliseconds: 250),
5555
curve: Curves.ease,
5656
);
@@ -72,7 +72,7 @@ class _AnchorWaterfallPageState extends State<AnchorWaterfallPage>
7272

7373
_tabController.index = ObserverUtils.calcAnchorTabIndex(
7474
observeModel: resultModel,
75-
tabIndexs: tabIndexs,
75+
tabIndexes: tabIndexes,
7676
currentTabIndex: _tabController.index,
7777
);
7878
},

example/lib/features/scene/detail/header/detail_header.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ typedef DetailLogicConsumerMixin<W extends StatefulWidget>
1616

1717
enum DetailUpdateType {
1818
navBar,
19+
config,
20+
loading,
21+
module3,
22+
module6,
1923
}
2024

2125
enum DetailModuleType {

example/lib/features/scene/detail/logic/detail_logic.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class DetailLogic extends GetxController with GetTickerProviderStateMixin {
2121
}
2222

2323
void onDispose() {
24+
state.isDisposed = true;
2425
onDisposeForNavBar();
2526
onDisposeForListView();
2627
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* @Author: LinXunFeng linxunfeng@yeah.net
3+
* @Repo: https://github.com/fluttercandies/flutter_scrollview_observer
4+
* @Date: 2025-08-05 22:38:33
5+
*/
6+
7+
import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart';
8+
import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic_list_view.dart';
9+
10+
extension DetailLogicForConfig on DetailLogic {
11+
void onConfigConfirm() {
12+
state.defaultModuleAnchor = state.configSelectedAnchor;
13+
state.showConfig = false;
14+
15+
initIndexPositionForListView();
16+
17+
// The synchronization data required for the ListView is loaded.
18+
update();
19+
20+
// Now it's the first time the ListView is rendered.
21+
firstTimeRenderListView();
22+
23+
// Load asynchronous data.
24+
loadAsyncDataForListView();
25+
}
26+
}

example/lib/features/scene/detail/logic/detail_logic_list_view.dart

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
* @Date: 2025-08-03 21:02:30
55
*/
66

7+
import 'package:flutter/material.dart';
78
import 'package:scrollview_observer/scrollview_observer.dart';
9+
import 'package:scrollview_observer_example/common/route/route.dart';
10+
import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart';
811
import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart';
912
import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic_nav_bar.dart';
13+
import 'package:scrollview_observer_example/utils/snackbar.dart';
1014

1115
extension DetailLogicForListView on DetailLogic {
1216
void onInitForListView() {
@@ -24,10 +28,109 @@ extension DetailLogicForListView on DetailLogic {
2428
final navBarTabController = state.navBarTabController;
2529
if (navBarTabController == null) return;
2630

27-
navBarTabController.index = ObserverUtils.calcAnchorTabIndex(
31+
final index = ObserverUtils.calcAnchorTabIndex(
2832
observeModel: result,
29-
tabIndexs: state.navBarTabs.map((e) => e.index).toList(),
33+
tabIndexes: state.navBarTabs.map((e) => e.index).toList(),
3034
currentTabIndex: navBarTabController.index,
3135
);
36+
updateNavBarTabIndex(index);
37+
}
38+
39+
void initIndexPositionForListView() {
40+
final defaultIndexModel = ObserverIndexPositionModel(
41+
index: 0,
42+
);
43+
ObserverIndexPositionModel indexModel = defaultIndexModel;
44+
45+
() {
46+
final moduleAnchor = state.defaultModuleAnchor;
47+
if (moduleAnchor == null) return;
48+
49+
// Modules loaded asynchronously do not need to be processed.
50+
if (state.asyncLoadModuleTypes.contains(moduleAnchor)) return;
51+
indexModel = ObserverIndexPositionModel(
52+
index: state.moduleTypes.indexOf(moduleAnchor),
53+
offset: (_) => state.navBarHeight,
54+
);
55+
}();
56+
57+
state.observerController.initialIndexModel = indexModel;
58+
}
59+
60+
/// Specifically designed to handle those module anchors that are loaded
61+
/// asynchronously.
62+
void checkAnchorForListView(DetailModuleType moduleType) async {
63+
final moduleAnchor = state.defaultModuleAnchor;
64+
if (moduleAnchor == null) return;
65+
if (moduleType != moduleAnchor) return;
66+
if (!state.moduleTypes.contains(moduleType)) return;
67+
final index = state.moduleTypes.indexOf(moduleType);
68+
await WidgetsBinding.instance.endOfFrame;
69+
state.observerController.animateTo(
70+
index: index,
71+
duration: const Duration(milliseconds: 300),
72+
curve: Curves.easeInOut,
73+
offset: (_) => state.navBarHeight,
74+
);
75+
}
76+
77+
/// When updating the module widget, keep the current position unchanged.
78+
void updateAndKeepPositionForListView([
79+
List<Object>? ids,
80+
]) {
81+
() async {
82+
final result = await state.observerController.dispatchOnceObserve(
83+
isForce: true,
84+
isDependObserveCallback: false,
85+
);
86+
final observeResult = result.observeResult;
87+
if (observeResult == null) return;
88+
final firstChild = observeResult.firstChild;
89+
if (firstChild == null) return;
90+
final refItemIndex = firstChild.index;
91+
// Keep position
92+
state.keepPositionObserver.standby(
93+
mode: ChatScrollObserverHandleMode.specified,
94+
refItemIndex: refItemIndex,
95+
refItemIndexAfterUpdate: refItemIndex,
96+
);
97+
}();
98+
99+
update(ids);
100+
}
101+
102+
/// Show loading first, and hide loading after the ListView is rendered.
103+
void firstTimeRenderListView() {
104+
WidgetsBinding.instance.addPostFrameCallback((_) {
105+
state.showLoading = false;
106+
update([DetailUpdateType.loading]);
107+
});
108+
}
109+
110+
void loadAsyncDataForListView() {
111+
// Load Module3 and Module6 asynchronously
112+
Future.delayed(const Duration(seconds: 2)).then((_) {
113+
if (state.isDisposed) return;
114+
state.haveDataForModule3 = true;
115+
// update([DetailUpdateType.module3]);
116+
updateAndKeepPositionForListView([DetailUpdateType.module3]);
117+
checkAnchorForListView(DetailModuleType.module3);
118+
SnackBarUtil.showSnackBar(
119+
context: NavigationService.context,
120+
text: 'Module3 has been displayed',
121+
);
122+
});
123+
124+
Future.delayed(const Duration(seconds: 3)).then((_) {
125+
if (state.isDisposed) return;
126+
state.haveDataForModule6 = true;
127+
// update([DetailUpdateType.module6]);
128+
updateAndKeepPositionForListView([DetailUpdateType.module6]);
129+
checkAnchorForListView(DetailModuleType.module6);
130+
SnackBarUtil.showSnackBar(
131+
context: NavigationService.context,
132+
text: 'Module6 has been displayed',
133+
);
134+
});
32135
}
33136
}

example/lib/features/scene/detail/logic/detail_logic_nav_bar.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ extension DetailLogicForNavBar on DetailLogic {
5353
);
5454
}
5555

56+
void updateNavBarTabIndex(int index) {
57+
final navBarTabController = state.navBarTabController;
58+
if (navBarTabController == null) return;
59+
navBarTabController.index = index;
60+
}
61+
5662
void updateNavBarAlpha() {
5763
if (!state.scrollController.position.hasPixels) return;
5864
state.scrollController.position.pixels;

example/lib/features/scene/detail/page/detail_page.dart

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
* @Date: 2025-08-02 19:57:05
55
*/
66

7+
import 'package:flutter/cupertino.dart';
78
import 'package:flutter/material.dart';
89
import 'package:get/get.dart';
910
import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart';
1011
import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart';
1112
import 'package:scrollview_observer_example/features/scene/detail/state/detail_state.dart';
13+
import 'package:scrollview_observer_example/features/scene/detail/widget/detail_config_view.dart';
1214
import 'package:scrollview_observer_example/features/scene/detail/widget/detail_list_view.dart';
1315
import 'package:scrollview_observer_example/features/scene/detail/widget/detail_nav_bar.dart';
1416

@@ -53,16 +55,37 @@ class DetailPageState extends State<DetailPage>
5355
}
5456

5557
Widget _buildBody() {
56-
return const Stack(
58+
if (state.showConfig) {
59+
return const DetailConfigView();
60+
}
61+
return Stack(
5762
children: [
58-
DetailListView(),
59-
Positioned(
63+
const DetailListView(),
64+
const Positioned(
6065
top: 0,
6166
left: 0,
6267
right: 0,
6368
child: DetailNavBar(),
6469
),
70+
_buildLoading(),
6571
],
6672
);
6773
}
74+
75+
Widget _buildLoading() {
76+
return GetBuilder<DetailLogic>(
77+
tag: logicTag,
78+
id: DetailUpdateType.loading,
79+
builder: (_) {
80+
if (!state.showLoading) return const SizedBox.shrink();
81+
82+
return Positioned.fill(
83+
child: Container(
84+
color: Colors.white,
85+
child: const CupertinoActivityIndicator(),
86+
),
87+
);
88+
},
89+
);
90+
}
6891
}

example/lib/features/scene/detail/state/detail_state.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
* @Date: 2025-08-02 19:57:05
55
*/
66

7+
import 'package:scrollview_observer_example/features/scene/detail/state/detail_state_config.dart';
78
import 'package:scrollview_observer_example/features/scene/detail/state/detail_state_list_view.dart';
89
import 'package:scrollview_observer_example/features/scene/detail/state/detail_state_nav_bar.dart';
910

10-
class DetailState with DetailStateForNavBar, DetailStateForListView {}
11+
class DetailState
12+
with DetailStateForNavBar, DetailStateForListView, DetailStateForConfig {
13+
bool isDisposed = false;
14+
15+
bool showLoading = true;
16+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* @Author: LinXunFeng linxunfeng@yeah.net
3+
* @Repo: https://github.com/fluttercandies/flutter_scrollview_observer
4+
* @Date: 2025-08-05 22:42:42
5+
*/
6+
7+
import 'package:flutter/material.dart';
8+
import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart';
9+
10+
mixin DetailStateForConfig {
11+
bool showConfig = true;
12+
13+
DetailModuleType configSelectedAnchor = DetailModuleType.module7;
14+
15+
List<DropdownMenuEntry<DetailModuleType>> get configDefaultAnchorEntries {
16+
List<DropdownMenuEntry<DetailModuleType>> entries = [];
17+
for (final DetailModuleType moduleType in DetailModuleType.values) {
18+
entries.add(
19+
DropdownMenuEntry(value: moduleType, label: moduleType.name),
20+
);
21+
}
22+
return entries;
23+
}
24+
}

0 commit comments

Comments
 (0)