WebView in Flutter

WebView in Flutter: A Complete Tutorial for 2026

WebView in Flutter: A Complete Tutorial for 2026

Last Updated: | Author: Deep Singh

Sometimes your Flutter app needs to show a web page — maybe a help center, a terms of service page, or a payment gateway. That's where WebView comes in. It lets you embed a browser directly inside your app so users never have to leave.

In this tutorial, we'll walk through how to add a WebView in Flutter using the latest webview_flutter plugin. I'll show you the full working code, explain what each part does, and point out the things that have changed recently so your code actually compiles.

Before You Start

Make sure you have the following ready:

  • Flutter SDK installed (use the latest stable version for best compatibility)
  • Android Studio or VS Code with the Flutter plugin
  • A basic understanding of Dart and Flutter widgets
  • For Android: Set your minSdkVersion to at least 21 in android/app/build.gradle
  • For iOS: Set your deployment target to at least iOS 13.0 in your Podfile (iOS 15.0 is recommended for App Store compliance)

Adding the WebView Plugin

The easiest way to add the plugin is to run this command in your project folder:

flutter pub add webview_flutter

Or, if you prefer, open your pubspec.yaml file and add it manually under dependencies:

dependencies:
  webview_flutter: ^4.14.1

Then run flutter pub get to fetch the package.

Adding webview_flutter plugin in pubspec.yaml file
Adding the plugin to pubspec.yaml
Running flutter pub get to install webview_flutter
Fetching the package with flutter pub get

The Full Code

Here's a complete, working example. It loads Google in a WebView, shows a loading spinner while the page loads, and lets the user navigate back using the Android back button.

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(const MaterialApp(
    home: MyWebViewTutorial(),
    title: 'WebView Tutorial',
  ));
}

class MyWebViewTutorial extends StatefulWidget {
  const MyWebViewTutorial({super.key});

  @override
  State<MyWebViewTutorial> createState() => _MyWebViewTutorialState();
}

class _MyWebViewTutorialState extends State<MyWebViewTutorial> {
  late final WebViewController _controller;
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();

    // Set up the WebView controller
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(
        NavigationDelegate(
          onPageStarted: (String url) {
            setState(() => _isLoading = true);
          },
          onPageFinished: (String url) {
            setState(() => _isLoading = false);
          },
          onWebResourceError: (WebResourceError error) {
            debugPrint('WebView error: ${error.description}');
          },
        ),
      )
      ..loadRequest(Uri.parse('https://www.google.com/'));
  }

  // Handle the Android back button so it navigates back inside the WebView
  Future<bool> _onWillPop() async {
    if (await _controller.canGoBack()) {
      _controller.goBack();
      return false;
    }
    return true;
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: _onWillPop,
      child: Scaffold(
        appBar: AppBar(
          title: const Text('WebView Tutorial'),
        ),
        body: Stack(
          children: [
            WebViewWidget(controller: _controller),
            if (_isLoading)
              const Center(child: CircularProgressIndicator()),
          ],
        ),
      ),
    );
  }
}

What's Changed in Recent Versions

If you've seen older WebView tutorials online, you might notice the code looks different. That's because the plugin had a major update. Here's what changed:

  • The old WebView widget is gone. Older tutorials used WebView(...) directly, but that class was removed. If you try to use it, your code won't compile.
  • Now you use two separate pieces. You create a WebViewController to handle all the logic (loading, navigation, JavaScript), and a WebViewWidget to display the content on screen.
  • The controller is easier to set up. You can now create it right in initState() instead of waiting for a callback.
  • Some methods got new names. For example, loadUrl() is now loadRequest(), and navigation callbacks are set through setNavigationDelegate().

Wrapping Up

That's it — you've now got a working WebView inside your Flutter app. The plugin is actively maintained by the Flutter team, so keep your dependencies up to date to stay compatible with the latest Android and iOS requirements.

If you need platform-specific features like inline media playback on iOS or media gesture controls on Android, you can access them through the controller's platform property.

No comments: