Skip to content

Commit a2e7291

Browse files
authored
Add support for HtmlWidgetState.scrollToAnchor (#577)
Related to #536
1 parent d2f4d78 commit a2e7291

File tree

5 files changed

+73
-14
lines changed

5 files changed

+73
-14
lines changed

demo_app/lib/screens/hello_world.dart

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,49 @@ const kHtml = '''
140140
<br />
141141
''';
142142

143+
final globalKey = GlobalKey<HtmlWidgetState>();
144+
143145
class HelloWorldScreen extends StatelessWidget {
144146
const HelloWorldScreen({Key key}) : super(key: key);
145147

146148
@override
147149
Widget build(BuildContext context) => Scaffold(
148-
appBar: AppBar(title: const Text('HelloWorldScreen')),
150+
appBar: AppBar(
151+
title: const Text('HelloWorldScreen'),
152+
actions: const [PopupMenu()],
153+
),
149154
body: SingleChildScrollView(
150155
child: Padding(
151156
padding: const EdgeInsets.all(8.0),
152-
child: HtmlWidget(kHtml, webView: true),
157+
child: HtmlWidget(kHtml, key: globalKey, webView: true),
153158
),
154159
),
155160
);
156161
}
162+
163+
class PopupMenu extends StatelessWidget {
164+
const PopupMenu({Key key}) : super(key: key);
165+
166+
@override
167+
Widget build(BuildContext context) {
168+
return PopupMenuButton<_PopupMenuValue>(
169+
onSelected: (value) {
170+
switch (value) {
171+
case _PopupMenuValue.scrollToTop:
172+
globalKey.currentState?.scrollToAnchor('top');
173+
break;
174+
}
175+
},
176+
itemBuilder: (_) => const [
177+
PopupMenuItem(
178+
value: _PopupMenuValue.scrollToTop,
179+
child: Text('Scroll to #top'),
180+
),
181+
],
182+
);
183+
}
184+
}
185+
186+
enum _PopupMenuValue {
187+
scrollToTop,
188+
}

demo_app/lib/screens/hello_world_core.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ class HelloWorldCoreScreen extends StatelessWidget {
88

99
@override
1010
Widget build(BuildContext context) => Scaffold(
11-
appBar: AppBar(title: const Text('HelloWorldCoreScreen')),
12-
body: const SingleChildScrollView(
11+
appBar: AppBar(
12+
title: const Text('HelloWorldCoreScreen'),
13+
actions: const [enhanced.PopupMenu()],
14+
),
15+
body: SingleChildScrollView(
1316
child: Padding(
14-
padding: EdgeInsets.all(8.0),
15-
child: HtmlWidget(enhanced.kHtml),
17+
padding: const EdgeInsets.all(8.0),
18+
child: HtmlWidget(enhanced.kHtml, key: enhanced.globalKey),
1619
),
1720
),
1821
);

packages/core/lib/src/core_html_widget.dart

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,17 @@ class HtmlWidget extends StatefulWidget {
111111
super(key: key);
112112

113113
@override
114-
State<HtmlWidget> createState() => _HtmlWidgetState();
114+
State<HtmlWidget> createState() => HtmlWidgetState();
115115
}
116116

117-
class _HtmlWidgetState extends State<HtmlWidget> {
117+
/// State for a [HtmlWidget].
118+
class HtmlWidgetState extends State<HtmlWidget> {
119+
late final BuildMetadata _rootMeta;
120+
late final _RootTsb _rootTsb;
121+
late final WidgetFactory _wf;
122+
118123
Widget? _cache;
119124
Future<Widget>? _future;
120-
late BuildMetadata _rootMeta;
121-
late _RootTsb _rootTsb;
122-
late WidgetFactory _wf;
123125

124126
bool get buildAsync =>
125127
widget.buildAsync ?? widget.html.length > kShouldBuildAsync;
@@ -201,6 +203,9 @@ class _HtmlWidgetState extends State<HtmlWidget> {
201203
return _tshWidget(_cache!);
202204
}
203205

206+
/// Scrolls to make sure the requested anchor is as visible as possible.
207+
Future<bool> scrollToAnchor(String id) => _wf.onTapUrl('#$id');
208+
204209
Future<Widget> _buildAsync() async {
205210
final domNodes = await compute(_parseHtml, widget.html);
206211

@@ -234,7 +239,7 @@ class _HtmlWidgetState extends State<HtmlWidget> {
234239
class _RootTsb extends TextStyleBuilder {
235240
TextStyleHtml? _output;
236241

237-
_RootTsb(_HtmlWidgetState state) {
242+
_RootTsb(HtmlWidgetState state) {
238243
enqueue(builder, state);
239244
}
240245

@@ -244,7 +249,7 @@ class _RootTsb extends TextStyleBuilder {
244249
return super.build(context);
245250
}
246251

247-
TextStyleHtml builder(TextStyleHtml? _, _HtmlWidgetState state) {
252+
TextStyleHtml builder(TextStyleHtml? _, HtmlWidgetState state) {
248253
if (_output != null) return _output!;
249254
return _output = TextStyleHtml.root(
250255
state._wf.getDependencies(state.context),
@@ -255,7 +260,7 @@ class _RootTsb extends TextStyleBuilder {
255260
void reset() => _output = null;
256261
}
257262

258-
Widget _buildBody(_HtmlWidgetState state, dom.NodeList domNodes) {
263+
Widget _buildBody(HtmlWidgetState state, dom.NodeList domNodes) {
259264
final rootMeta = state._rootMeta;
260265
final wf = state._wf;
261266
wf.reset(state);

packages/core/test/anchor_test.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,19 @@ Future<void> main() async {
101101
expect(_onTapAnchorResults, equals({'target': true}));
102102
});
103103

104+
testWidgets('HtmlWidgetState.scrollToAnchor', (tester) async {
105+
await tester.pumpWidgetBuilder(
106+
const _ColumnTestApp(),
107+
surfaceSize: const Size(200, 200),
108+
);
109+
110+
final scrollFuture = globalKey.currentState?.scrollToAnchor('target');
111+
await tester.pumpAndSettle();
112+
113+
expect(await scrollFuture, isTrue);
114+
expect(_onTapAnchorResults, equals({'target': true}));
115+
});
116+
104117
testWidgets('ListView', (WidgetTester tester) async {
105118
await tester.pumpWidgetBuilder(
106119
const _ListViewTestApp(),
@@ -357,6 +370,8 @@ ${htmlDesc * 3}
357370
<a href="#target">Scroll up</a>
358371
''';
359372

373+
final globalKey = GlobalKey<HtmlWidgetState>();
374+
360375
class _ColumnTestApp extends StatelessWidget {
361376
final String? html;
362377
final Key? keyBottom;
@@ -371,6 +386,7 @@ class _ColumnTestApp extends StatelessWidget {
371386
HtmlWidget(
372387
html ?? htmlDefault,
373388
factoryBuilder: () => _WidgetFactory(),
389+
key: globalKey,
374390
),
375391
SizedBox.shrink(key: keyBottom),
376392
],

packages/enhanced/lib/src/html_widget.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import 'data.dart';
88
import 'helpers.dart';
99
import 'widget_factory.dart';
1010

11+
export 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'
12+
show HtmlWidgetState;
13+
1114
/// A widget that builds Flutter widget tree from HTML
1215
/// with support for IFRAME, VIDEO and many other tags.
1316
class HtmlWidget extends core.HtmlWidget {

0 commit comments

Comments
 (0)