WebView Flutter : Flutter Today App Step 1

What does a WebView in Flutter do? Certainly it displays web content on the mobile screen. But does its job end there?

Or, will we just display web content on the screen?

No. 

We can use the WebView plugin to add the webview Widget. In addition, we can design our Flutter app. We can add more widgets, menu items, tab controllers, etc.

In this first step, we will only concentrate on displaying web content using webview.

Firstly we need to add the dependencies in our “pubspec.yaml” file.

dependencies:
  flutter:
    sdk: flutter


  cupertino_icons: ^1.0.2
  webview_flutter: ^3.0.2
  path_provider: ^2.0.9
  google_fonts: ^2.3.2

Secondly, we want to apply a custom theme throughout the app. Therefore, we need a custom model theme.

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

/// In a custom theme page we have described color and fonts
/// We may add more custom theme-features later
///

class HappyTheme {
  static TextStyle titleStyle = GoogleFonts.langar(
    textStyle: const TextStyle(
      color: HappyTheme.shrineBrown600,
      fontSize: 20.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle greedStyle = GoogleFonts.laila(
    textStyle: const TextStyle(
      color: HappyTheme.shrinePink100,
      fontSize: 60.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle genderStyle = GoogleFonts.antic(
    textStyle: const TextStyle(
      color: HappyTheme.shrineSurfaceWhite,
      fontSize: 15.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle postContentStyle = GoogleFonts.lato(
    textStyle: const TextStyle(
      color: HappyTheme.shrineBrown600,
      fontSize: 20.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle appbarStyle = GoogleFonts.lato(
    textStyle: const TextStyle(
      color: HappyTheme.shrineSurfaceWhite,
      fontSize: 20.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle contentStyle = GoogleFonts.lato(
    textStyle: const TextStyle(
      color: HappyTheme.shrineBrown900,
      fontSize: 18.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle postTitleStyle = GoogleFonts.laila(
    textStyle: const TextStyle(
      color: HappyTheme.shrinePink400,
      fontSize: 35.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle happinessIndexStyle = GoogleFonts.antic(
    textStyle: const TextStyle(
      color: HappyTheme.shrineSurfaceWhite,
      fontSize: 80.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle happinessResultStyle = GoogleFonts.lato(
    textStyle: const TextStyle(
      color: HappyTheme.shrineSurfaceWhite,
      fontSize: 25.0,
      fontWeight: FontWeight.bold,
    ),
  );

  ThemeData _buildShrineTheme() {
    final ThemeData base = ThemeData.light();
    return base.copyWith(
      colorScheme: _shrineColorScheme,
      toggleableActiveColor: shrinePink400,
      primaryColor: shrinePink100,
      primaryColorLight: shrinePink100,
      scaffoldBackgroundColor: shrineBackgroundWhite,
      cardColor: shrineBackgroundWhite,
      textSelectionTheme:
          const TextSelectionThemeData(selectionColor: shrinePink100),
      errorColor: shrineErrorRed,
      buttonTheme: ButtonThemeData(
        colorScheme: _shrineColorScheme.copyWith(primary: shrinePink400),
        textTheme: ButtonTextTheme.normal,
      ),
      primaryIconTheme: _customIconTheme(base.iconTheme),
      textTheme: _buildShrineTextTheme(base.textTheme),
      primaryTextTheme: _buildShrineTextTheme(base.primaryTextTheme),
      iconTheme: _customIconTheme(base.iconTheme),
    );
  }

  ThemeData buildTheme() {
    return _buildShrineTheme();
  }

  IconThemeData _customIconTheme(IconThemeData original) {
    return original.copyWith(color: shrineBrown900);
  }

  TextTheme _buildShrineTextTheme(TextTheme base) {
    return base
        .copyWith(
          caption: base.caption!.copyWith(
            fontWeight: FontWeight.w400,
            fontSize: 14,
            letterSpacing: defaultLetterSpacing,
          ),
          button: base.button!.copyWith(
            fontWeight: FontWeight.w500,
            fontSize: 14,
            letterSpacing: defaultLetterSpacing,
          ),
        )
        .apply(
          fontFamily: 'Rubik',
          displayColor: shrineBrown900,
          bodyColor: shrineBrown900,
        );
  }

  static const ColorScheme _shrineColorScheme = ColorScheme(
    primary: shrinePink100,
    secondary: shrinePink50,
    surface: shrineSurfaceWhite,
    background: shrineBackgroundWhite,
    error: shrineErrorRed,
    onPrimary: shrineBrown900,
    onSecondary: shrineBrown900,
    onSurface: shrineBrown900,
    onBackground: shrineBrown900,
    onError: shrineSurfaceWhite,
    brightness: Brightness.light,
  );

  static const answerColor = Color(0xFFFFFFFF);

  static const Color activeCoor = Color(0xFFaa1111);
  static const Color inactiveCoor = Color(0xFF893131);

  static const Color shrinePink50 = Color(0xFFFEEAE6);
  static const Color shrinePink100 = Color(0xFFFEDBD0);
  static const Color shrinePink300 = Color(0xFFFBB8AC);
  static const Color shrinePink400 = Color(0xFFEAA4A4);

  static const Color shrineBrown900 = Color(0xFF4f0808);
  static const Color shrineBrown600 = Color(0xFF893131);

  static const Color shrineErrorRed = Color(0xFFC5032B);

  static const Color shrineSurfaceWhite = Color(0xFFFFFBFA);
  static const Color shrineBackgroundWhite = Colors.white;

  static const defaultLetterSpacing = 0.03;
}

In this first step we will shorten our code and declare the theme object inside the top level main() function.

However, later we’ll change it.

import 'package:flutter/material.dart';
import 'model/happy_theme.dart';
import 'view/flutter_today.dart';

void main() {
  HappyTheme happyTheme = HappyTheme();
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Today',
      theme: happyTheme.buildTheme(),
      home: const FlutterToday(),
    ),
  );
}

The top level function main() takes us to the view folder where we have defined the custom widgets.

Adding webview to the Flutter app

At present we’re adding the webview to the flutter app for displaying web content on Android devices. For that reason, we should induct minor changes.

For example, we have to add this line to our “android/app/build.gradle”.

defaultConfig {        
        minSdkVersion 19        
    }

The minimum SDK version should be 19. Otherwise meanwhile we run the app, it will give us errors.

Next we need to understand the Dart asynchronous Completer<T> class. In the next section we will discuss this topic in detail. 

Why? 

Because, the Completer<T> class will take a Type that we need to supply here.

This Type is WebViewController. We need this controller because the WebView widget enables the control with this controller.

As a result, we need to declare and pass it as a class constructor.

import 'dart:async';

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

import 'package:webview_flutter/webview_flutter.dart';
import 'web_view_home.dart';

class FlutterToday extends StatefulWidget {
  const FlutterToday({Key? key}) : super(key: key);

  @override
  State<FlutterToday> createState() => _FlutterTodayState();
}

class _FlutterTodayState extends State<FlutterToday> {
  final controller = Completer<WebViewController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'FlutterToday',
          style: GoogleFonts.laila(
            fontSize: 30.0,
            fontWeight: FontWeight.bold,
            color: Colors.black54,
          ),
        ),
      ),
      body: WebViewHome(webViewController: controller),
    );
  }
}

In our webview home page the WebView widget provides several page load events. 

Furthermore, the Flutter Today app will listen to these events.

During the page load cycle, the WebView widget fires three events. 

In addition, we can implement a page load indicator also.

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

class WebViewHome extends StatefulWidget {
  const WebViewHome({required this.webViewController, Key? key})
      : super(key: key);

  final Completer<WebViewController> webViewController;

  @override
  State<WebViewHome> createState() => _WebViewHomeState();
}

class _WebViewHomeState extends State<WebViewHome> {
  var loadingPercentage = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        WebView(
          initialUrl: 'https://sanjibsinha.com',
          onWebViewCreated: (webController) {
            widget.webViewController.complete(webController);
          },
          onPageStarted: (url) {
            setState(() {
              loadingPercentage = 0;
            });
          },
          onProgress: (progress) {
            setState(() {
              loadingPercentage = progress;
            });
          },
          onPageFinished: (url) {
            setState(() {
              loadingPercentage = 100;
            });
          },
          javascriptMode: JavascriptMode.unrestricted,
          javascriptChannels: _createJavascriptChannels(context),
        ),
        if (loadingPercentage < 100)
          LinearProgressIndicator(
            value: loadingPercentage / 100.0,
          ),
      ],
    );
  }

  Set<JavascriptChannel> _createJavascriptChannels(BuildContext context) {
    return {
      JavascriptChannel(
        name: 'SnackBar',
        onMessageReceived: (message) {
          ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(message.message)));
        },
      ),
    };
  }
}

As an outcome, when we run the app, we get the web content.

WebView Flutter displays web content
WebView Flutter displays web content

We have not finished yet. On the whole, we get an idea how the webview widget in flutter works.

However, we’ll try to make it complete and make it a finished-app.

So stay tuned with each step, where we’ll dig deep and learn to use webview completely.

By that time, if you want to clone this step, please visit the GitHub Repository.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

Courses at Educative

GitHub repository

Technical blog

Twitter


by

Comments

One response to “WebView Flutter : Flutter Today App Step 1”

  1. […] We have been using webview in flutter to display web content on the screen. However, in our previous section we have just learned how to use webview in flutter. […]

Leave a Reply