|
@@ -5,7 +5,7 @@ import 'package:flutter/gestures.dart'; |
|
@@ -5,7 +5,7 @@ import 'package:flutter/gestures.dart'; |
|
5
|
import 'package:flutter/material.dart';
|
5
|
import 'package:flutter/material.dart';
|
|
6
|
import 'package:get/get.dart';
|
6
|
import 'package:get/get.dart';
|
|
7
|
|
7
|
|
|
8
|
-void showTodayFaqBottomSheet() {
|
8
|
+void showTodayFaqBottomSheet({String? text}) {
|
|
9
|
final view = WidgetsBinding.instance.platformDispatcher.views.first;
|
9
|
final view = WidgetsBinding.instance.platformDispatcher.views.first;
|
|
10
|
final devicePixelRatio = view.devicePixelRatio;
|
10
|
final devicePixelRatio = view.devicePixelRatio;
|
|
11
|
final screenHeight = view.physicalSize.height / devicePixelRatio;
|
11
|
final screenHeight = view.physicalSize.height / devicePixelRatio;
|
|
@@ -20,6 +20,7 @@ void showTodayFaqBottomSheet() { |
|
@@ -20,6 +20,7 @@ void showTodayFaqBottomSheet() { |
|
20
|
builder: (context, scrollController) {
|
20
|
builder: (context, scrollController) {
|
|
21
|
return TodayFaqBottomSheet(
|
21
|
return TodayFaqBottomSheet(
|
|
22
|
scrollController: scrollController,
|
22
|
scrollController: scrollController,
|
|
|
|
23
|
+ scrollToText: text,
|
|
23
|
);
|
24
|
);
|
|
24
|
},
|
25
|
},
|
|
25
|
),
|
26
|
),
|
|
@@ -30,13 +31,78 @@ void showTodayFaqBottomSheet() { |
|
@@ -30,13 +31,78 @@ void showTodayFaqBottomSheet() { |
|
30
|
);
|
31
|
);
|
|
31
|
}
|
32
|
}
|
|
32
|
|
33
|
|
|
33
|
-class TodayFaqBottomSheet extends StatelessWidget {
|
34
|
+class TodayFaqBottomSheet extends StatefulWidget {
|
|
34
|
const TodayFaqBottomSheet({
|
35
|
const TodayFaqBottomSheet({
|
|
35
|
super.key,
|
36
|
super.key,
|
|
36
|
required this.scrollController,
|
37
|
required this.scrollController,
|
|
|
|
38
|
+ this.scrollToText,
|
|
37
|
});
|
39
|
});
|
|
38
|
|
40
|
|
|
39
|
final ScrollController scrollController;
|
41
|
final ScrollController scrollController;
|
|
|
|
42
|
+ final String? scrollToText;
|
|
|
|
43
|
+
|
|
|
|
44
|
+ @override
|
|
|
|
45
|
+ State<TodayFaqBottomSheet> createState() => _TodayFaqBottomSheetState();
|
|
|
|
46
|
+}
|
|
|
|
47
|
+
|
|
|
|
48
|
+class _TodayFaqBottomSheetState extends State<TodayFaqBottomSheet> {
|
|
|
|
49
|
+ // 为每个 FAQ 卡片分配 GlobalKey,用于定位渲染位置
|
|
|
|
50
|
+ final _noDataKey = GlobalKey();
|
|
|
|
51
|
+ final _watchNoNotificationKey = GlobalKey();
|
|
|
|
52
|
+ final _watchFaceDelayKey = GlobalKey();
|
|
|
|
53
|
+ final _watchFaceBlackScreenKey = GlobalKey();
|
|
|
|
54
|
+
|
|
|
|
55
|
+ @override
|
|
|
|
56
|
+ void initState() {
|
|
|
|
57
|
+ super.initState();
|
|
|
|
58
|
+ if (widget.scrollToText != null) {
|
|
|
|
59
|
+ // 等第一帧渲染完成后,计算目标卡片位置并滚动
|
|
|
|
60
|
+ WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToTarget());
|
|
|
|
61
|
+ }
|
|
|
|
62
|
+ }
|
|
|
|
63
|
+
|
|
|
|
64
|
+ void _scrollToTarget() {
|
|
|
|
65
|
+ final l10n = context.l10n;
|
|
|
|
66
|
+ final text = widget.scrollToText;
|
|
|
|
67
|
+
|
|
|
|
68
|
+ GlobalKey? targetKey;
|
|
|
|
69
|
+ if (text == l10n.todayFaqLinkNoData) {
|
|
|
|
70
|
+ targetKey = _noDataKey;
|
|
|
|
71
|
+ } else if (text == l10n.todayFaqLinkWatchNoStatusNotification ||
|
|
|
|
72
|
+ text == l10n.todayFaqLinkWatchNoStatusAndInteractionNotification) {
|
|
|
|
73
|
+ targetKey = _watchNoNotificationKey;
|
|
|
|
74
|
+ } else if (text == l10n.todayFaqLinkWatchFaceDataDelay) {
|
|
|
|
75
|
+ targetKey = _watchFaceDelayKey;
|
|
|
|
76
|
+ } else if (text == l10n.todayFaqLinkWatchFaceBlackScreen) {
|
|
|
|
77
|
+ targetKey = _watchFaceBlackScreenKey;
|
|
|
|
78
|
+ }
|
|
|
|
79
|
+
|
|
|
|
80
|
+ if (targetKey == null) return;
|
|
|
|
81
|
+
|
|
|
|
82
|
+ final renderBox =
|
|
|
|
83
|
+ targetKey.currentContext?.findRenderObject() as RenderBox?;
|
|
|
|
84
|
+ if (renderBox == null) return;
|
|
|
|
85
|
+
|
|
|
|
86
|
+ // 将目标卡片的本地坐标转换为 ListView 滚动视图坐标系中的偏移量
|
|
|
|
87
|
+ final listRenderObject = widget
|
|
|
|
88
|
+ .scrollController.position.context.storageContext
|
|
|
|
89
|
+ .findRenderObject();
|
|
|
|
90
|
+ if (listRenderObject == null) return;
|
|
|
|
91
|
+
|
|
|
|
92
|
+ final targetOffset =
|
|
|
|
93
|
+ renderBox.localToGlobal(Offset.zero, ancestor: listRenderObject);
|
|
|
|
94
|
+ final currentOffset = widget.scrollController.offset;
|
|
|
|
95
|
+ final targetScrollOffset = (currentOffset + targetOffset.dy).clamp(
|
|
|
|
96
|
+ widget.scrollController.position.minScrollExtent,
|
|
|
|
97
|
+ widget.scrollController.position.maxScrollExtent,
|
|
|
|
98
|
+ );
|
|
|
|
99
|
+
|
|
|
|
100
|
+ widget.scrollController.animateTo(
|
|
|
|
101
|
+ targetScrollOffset,
|
|
|
|
102
|
+ duration: const Duration(milliseconds: 350),
|
|
|
|
103
|
+ curve: Curves.easeInOut,
|
|
|
|
104
|
+ );
|
|
|
|
105
|
+ }
|
|
40
|
|
106
|
|
|
41
|
@override
|
107
|
@override
|
|
42
|
Widget build(BuildContext context) {
|
108
|
Widget build(BuildContext context) {
|
|
@@ -90,11 +156,12 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
@@ -90,11 +156,12 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
90
|
),
|
156
|
),
|
|
91
|
Expanded(
|
157
|
Expanded(
|
|
92
|
child: ListView(
|
158
|
child: ListView(
|
|
93
|
- controller: scrollController,
|
159
|
+ controller: widget.scrollController,
|
|
94
|
physics: const ClampingScrollPhysics(),
|
160
|
physics: const ClampingScrollPhysics(),
|
|
95
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 20),
|
161
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 20),
|
|
96
|
children: [
|
162
|
children: [
|
|
97
|
_InfoCard(
|
163
|
_InfoCard(
|
|
|
|
164
|
+ key: _noDataKey,
|
|
98
|
title: l10n.todayFaqNoDataTitle,
|
165
|
title: l10n.todayFaqNoDataTitle,
|
|
99
|
children: [
|
166
|
children: [
|
|
100
|
_InfoText(
|
167
|
_InfoText(
|
|
@@ -110,6 +177,7 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
@@ -110,6 +177,7 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
110
|
),
|
177
|
),
|
|
111
|
const SizedBox(height: 12),
|
178
|
const SizedBox(height: 12),
|
|
112
|
_InfoCard(
|
179
|
_InfoCard(
|
|
|
|
180
|
+ key: _watchNoNotificationKey,
|
|
113
|
title: l10n.todayFaqWatchNoNotificationTitle,
|
181
|
title: l10n.todayFaqWatchNoNotificationTitle,
|
|
114
|
children: [
|
182
|
children: [
|
|
115
|
_InfoText(
|
183
|
_InfoText(
|
|
@@ -135,6 +203,7 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
@@ -135,6 +203,7 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
135
|
),
|
203
|
),
|
|
136
|
const SizedBox(height: 12),
|
204
|
const SizedBox(height: 12),
|
|
137
|
_InfoCard(
|
205
|
_InfoCard(
|
|
|
|
206
|
+ key: _watchFaceDelayKey,
|
|
138
|
title: l10n.todayFaqWatchFaceDelayTitle,
|
207
|
title: l10n.todayFaqWatchFaceDelayTitle,
|
|
139
|
children: [
|
208
|
children: [
|
|
140
|
_InfoText(
|
209
|
_InfoText(
|
|
@@ -156,6 +225,7 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
@@ -156,6 +225,7 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
156
|
),
|
225
|
),
|
|
157
|
const SizedBox(height: 12),
|
226
|
const SizedBox(height: 12),
|
|
158
|
_InfoCard(
|
227
|
_InfoCard(
|
|
|
|
228
|
+ key: _watchFaceBlackScreenKey,
|
|
159
|
title: l10n.todayFaqWatchFaceBlackScreenTitle,
|
229
|
title: l10n.todayFaqWatchFaceBlackScreenTitle,
|
|
160
|
children: [
|
230
|
children: [
|
|
161
|
_InfoText(
|
231
|
_InfoText(
|
|
@@ -175,6 +245,7 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
@@ -175,6 +245,7 @@ class TodayFaqBottomSheet extends StatelessWidget { |
|
175
|
|
245
|
|
|
176
|
class _InfoCard extends StatelessWidget {
|
246
|
class _InfoCard extends StatelessWidget {
|
|
177
|
const _InfoCard({
|
247
|
const _InfoCard({
|
|
|
|
248
|
+ super.key,
|
|
178
|
required this.title,
|
249
|
required this.title,
|
|
179
|
required this.children,
|
250
|
required this.children,
|
|
180
|
});
|
251
|
});
|