Dart: Load an html file into a Flutter Webview

5,230

IMO, this is the approach for this:

This is an example approach, I will be building a Flutter app that can read file and write data to file for later use.

It is an app that could write the String to text.txt file. Everytime the app is launched it will display the contents of text.txt.

I need a place to write data on disk and read it again when the app loads. So I used path_provider plugin to access Documents directory (on iOS, this corresponds to NSDocumentDirectory, on Android, this is the AppData directory).

Future<String> get _localPath async {
  final directory = await getApplicationDocumentsDirectory();
  return directory.path;
}

Create a reference to the File full location (in our case, the text.txt file), we use File class from the dart:io library.

Future<File> get _localFile async {
  final path = await _localPath;
  return File('$path/text.txt');
}

Need to write a string to a file using File writeAsString() method. It returns a Future<File> that completes with this File object once the entire operation has completed.

By default, writeAsString() creates the file and truncates the file if it already exists.

To append data to existing file, pass FileMode.append mode as second parameter.

Future<File> writeFile(String text) async {
  final file = await _localFile;
  return file.writeAsString('$text\r\n', mode: FileMode.append);
}

Use File readAsString() method to read the entire contents as a string. It returns a Future<String> that completes with the string once contents has been read.

Future<String> readFile() async {
  try {
    final file = await _localFile;
 
    String content = await file.readAsString();
    return content;
  } catch (e) {
    return '';
  }
}

Here is the complete sample code:

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:async';
import 'dart:io';

void main() {
  runApp(
    MaterialApp(
      title: 'Read/Write Files',
      home: MyApp(storage: TextStorage()),
    ),
  );
}

class TextStorage {
  Future<String> get _localPath async {
    final directory = await getApplicationDocumentsDirectory();
    return directory.path;
  }

  Future<File> get _localFile async {
    final path = await _localPath;
    return File('$path/text.txt');
  }

  Future<String> readFile() async {
    try {
      final file = await _localFile;

      String content = await file.readAsString();
      return content;
    } catch (e) {
      return '';
    }
  }

  Future<File> writeFile(String text) async {
    final file = await _localFile;
    return file.writeAsString('$text\r\n', mode: FileMode.append);
  }

  Future<File> cleanFile() async {
    final file = await _localFile;
    return file.writeAsString('');
  }
}

class MyApp extends StatefulWidget {
  final TextStorage storage;

  MyApp({Key key, @required this.storage}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  TextEditingController _textField = new TextEditingController();

  String _content = '';

  @override
  void initState() {
    super.initState();
    widget.storage.readFile().then((String text) {
      setState(() {
        _content = text;
      });
    });
  }

  Future<File> _writeStringToTextFile(String text) async {
    setState(() {
      _content += text + '\r\n';
    });

    return widget.storage.writeFile(text);
  }

  Future<File> _clearContentsInTextFile() async {
    setState(() {
      _content = '';
    });

    return widget.storage.cleanFile();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Read/Write File Example'),
        backgroundColor: Colors.blue,
      ),
      body: Container(
        padding: EdgeInsets.all(20.0),
        child: Column(
          children: <Widget>[
            TextField(
              controller: _textField,
            ),
            Padding(
              padding: EdgeInsets.all(20.0),
              child: RaisedButton(
                child: Text('Write to File'),
                color: Colors.lightBlueAccent,
                onPressed: () {
                  if (_textField.text.isNotEmpty) {
                    _writeStringToTextFile(_textField.text);
                    _textField.clear();
                  }
                },
              ),
            ),
            Padding(
              padding: EdgeInsets.only(bottom: 20.0),
              child: RaisedButton(
                child: Text(
                  'Clear Contents',
                  style: TextStyle(color: Colors.white),
                ),
                color: Colors.grey,
                onPressed: () {
                  _clearContentsInTextFile();
                },
              ),
            ),
            Expanded(
              flex: 1,
              child: new SingleChildScrollView(
                child: Text(
                  '$_content',
                  style: TextStyle(
                    color: Colors.blue,
                    fontSize: 22.0,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Here's how it looks when run:

enter image description here

You can play around depending on your requirement.

Also for your other question on how to load an html file in Flutter webview, here is my approach:

You can actually make it work using the webview_flutter plugin.

Create an assets folder in the root directory of your Flutter application, then put your .html file in the assets folder.

You should add your .html file to under assets in your pubspec.yaml file.

pubspec.yaml

  assets:
    - assets/SampleHtml.html

Here is a sample implementation to load an html from a local file:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<String> localLoader() async {
    return await rootBundle.loadString('assets/SampleHtml.html');
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
        future: localLoader(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return WebView(
              initialUrl:
                  new Uri.dataFromString(snapshot.data, mimeType: 'text/html')
                      .toString(),
              javascriptMode: JavascriptMode.unrestricted,
            );
          } else if (snapshot.hasError) {
            return Text("${snapshot.error}");
          }
          return CircularProgressIndicator();
        });
  }
}

SampleHtml.html

<!DOCTYPE html>
<meta charset="utf-8">
<title>Sample HTML</title>
<html>
    <head>
        <link rel="stylesheet">
    </head>
    <body>
        <h1>Flutter Webview</h1>
        <p>A Flutter plugin that provides a WebView widget.</p>
        <ul>
            <li>List 1</li>
            <li>List 2</li>
            <li>List 3</li>
        </ul>
    </body>
</html>

This is how it looks like:

enter image description here

Share:
5,230
Julien
Author by

Julien

The only thing standing between you and your dream is the bullshit. Story you keep telling yourself as to why you can't achieve it.

Updated on December 08, 2022

Comments

  • Julien
    Julien over 1 year

    Sthg makes me crazy, I try to write an html file on the disk using the path_provider plugin with the dart:io library.

    Here is what I tried (OKAY):

    Future<File> writeFile() async {
       final file = await _localFile;
       return file.writeAsString('<!DOCTYPE html><html lang="fr"> <head> <meta charset="UTF-8"> <title>test_design</title> ...');
     }
    

    then the file is loaded in a webview: OKAY

    But if I try with a longer html file, it doesn't work anymore, eg :

    return file.writeAsString(' html file containing js ');
    

    Any idea?

    or how to load an html file (the file is not static) in a Flutter webview ?


    I use flutter_webview_plugin.dart

    (https://pub.dartlang.org/packages/flutter_webview_plugin)

    webview code :

             writeFile().then((file){
                readFile().then((r){
                   _localPath.then((path){
                        var uri='file:///'+path+'/index.html';
    
                        flutterWebViewPlugin.launch(uri,
                            rect: new Rect.fromLTWH(
                              0.0,
                              400.0,
                              MediaQuery.of(context).size.width,
                              200.0,
                            ),
                          );
    
    • Anis Alibegić
      Anis Alibegić over 5 years
      Show us the webview code and webview plugin url.
    • Julien
      Julien over 5 years
      @Spectarion I updated the question with what you asked
    • Durdu
      Durdu over 5 years
  • ezaspi
    ezaspi about 2 years
    Thanks for this solution. Can you tell me what controls the size of the characters displayed? I tried your html file, but the text is very tiny. My device is a Pixel 5 running Chromium. My concern is if it is a readable size for you, is it the choice of browser?
  • ezaspi
    ezaspi about 2 years
    Regarding the small size, I found a solution at stackoverflow.com/questions/57752837/…