WebViews can capture specific gestures, too

Since WebViews are Flutter widgets, they can participate in Flutter’s gesture disambiguation protocol (aka the Gesture Arena). By default, a WebView only responds to a gesture if no other widget claimed it. But you can make the WebView proactively claim a gesture by specifying gestureRecognizers .

If your WebView is inside another widget that responds to gestures, for example a ListView, you might want to specify how your app responds to gestures. If the user drags their finger across the screen, should you scroll the ListView or the WebView? If you want both widgets to be scrollable, the WebView widget can “capture” the drag gesture so that it scrolls when the user drags the WebView, but scrolls the ListView otherwise. You can specify which gestures get passed on to the WebView widget with the gestureRecognizers parameter. The parameter takes a Set of all the GestureRecognizers that you want to capture. Don’t be scared off by that Factory object, either — it’s basically just a glorified builder method. To capture the vertical scroll event, I can write this:

WebView(

initialUrl: someUrl,

gestureRecognizers: Set()

..add(Factory<VerticalDragGestureRecognizer>(

() => VerticalDragGestureRecognizer())),

)

Or, written another way:

var verticalGestures = Factory<VerticalDragGestureRecognizer>(

() => VerticalDragGestureRecognizer());

var gestureSet = Set.from([verticalGestures]);

return WebView(

initialUrl: someUrl,

gestureRecognizers: gestureSet,

);

If you watch the Boring Flutter Development Show at all, you may have seen us develop the Kraken News, I mean the Hacker News Reader App.

latest version of the Hacker News app that we’ve been developing on our YouTube show

To show gesture capturing in the context of an app, I modified the Hacker News app to show part of the webpage as a “preview.” This allows the user to scroll through the linked page vertically to determine if they want to open it in a separate page for more in-depth reading.

Hacker News Reader app with WebView previews of the links. The WebViews capture the vertical drag gesture to enable scrolling of the previews.

The code for this Hacker News reader app can be found at this GitHub repo. (perhaps show code for _buildItem here).

However, if the WebView is inside a widget that only captures gestures you don’t care about, no gesture detector is needed. For example, a PageController only responds to horizontal drag gestures, and you just want the WebView to be able to scroll vertically, you can write the code like this:

PageView(children: [

WebView(initialUrl: urlOne),

WebView(initialUrl: urlTwo),

WebView(initialUrl: urlThree),

]));

WebViews in a PageView. The PageView controls the horizontal swiping and the WebViews can scroll vertically with no extra work on your part.

Your WebViews might need a key parameter

You’ve probably seen those ubiquitous optional key parameters sprinkled on just about every Widget constructor in the Flutter codebase. Keys are needed if you have multiple stateful widgets of the same type that are removed, added, or reordered in your app. As it turns out, WebView is a stateful widget (the state including the current page and browser history). Therefore, if you have multiple WebViews in your app, you may need to add a key parameter.

An example of this situation is actually in the Hacker News app! Here’s what happens if we switch tabs and our WebViews don’t have keys:

This is what happens if you don’t use keys in a stateful app. When we change to the “New Stories” tab the wrong web preview page displays.

As you can see, when we switch tabs, the “Interview with Alan Kay” tile is expanded, but the webview still shows the BBC page about Virgin Galactic! This is fixed by putting a key at the topmost collection widget (in this case the ExpansionTile ):

Whew! Using keys fixes the problem by showing a different WebView (and the corresponding correct website) for each item in the reader app

The super abbreviated explanation of why keys solve this is that when Flutter switches the list of stories to display, it sees that each set of stories is made of a ListView with ExpansionTile items. Flutter has a fast comparison algorithm to avoid unnecessarily redrawing the screen that checks the widget type and key. Without a key, because the widget types of each list are the same, the stateless items (like link titles) all get updated, but stateful components, (like the expanded state of ExpansionTile and the website’s URL), they fail to get redrawn. Adding a key fixes this issue. For a more thorough explanation, check out this video on keys:

Similarly, if you use WebViews in the context of a Hero widget, you’ll want to use a global key so that Flutter knows the two WebViews are actually the same and doesn’t try to re-render the second.

A few remaining things to bear in mind:

The WebView plugin is currently in Developer Preview while we finish adding polish. This means that if you want to run the webview plugin on iOS, you also need to add the following line inside the <dict> in your ios/Runner/Info.plist :

<key>io.flutter.embedded_views_preview</key><string>yes</string> as described in this GitHub issue.

There is another community-based WebView plugin though it doesn’t have all of the functionality of the above plugin by the Flutter team. It is simply displays a webpage in a native view, and is not integrated with the rest of the Flutter widget tree. Therefore that version does not allow you to compose WebViews with arbitrary other Flutter widgets. Using the webview_flutter plugin described in this article avoids this problem.

That’s all folks! Go forth and add WebViews to your Flutter apps. And give those krakens some love too.