flutter - How to download asset when apps launched and used it

4,467

The easiest way to do it is to download your files as zip (archived file) and unpack them in the path of the application storage directory getApplicationDocumentsDirectory

You will use this list of packages: archive , http and path_provider

The pubspec.yaml will look like

version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  path_provider: ^1.1.0
  http: ^0.12.0+2
  archive: ^2.0.8

dev_dependencies:
  flutter_test:
    sdk: flutter
  uses-material-design: true
  assets:
    - assets/images/

The main.dart file which coronations your app will look like Note that api is the URL of your file without the file name.

main.dart

import 'dart:io';

import 'package:archive/archive.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';

import 'data.dart';

const api =
    'https://firebasestorage.googleapis.com/v0/b/playground-a753d.appspot.com/o';

enum AppTheme { candy, cocktail }

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(AppTheme.candy),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final AppTheme theme;

  MyHomePage(this.theme);

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

class _MyHomePageState extends State<MyHomePage> {
  AppTheme _theme;
  String _dir;
  List<String> _images;

  @override
  void initState() {
    super.initState();
    _theme = widget.theme;
    _images = data[_theme];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.style),
        onPressed: () async {
          if (_theme == AppTheme.candy) {
            await _downloadAssets('cocktail');
          }
          setState(() {
            _theme =
                _theme == AppTheme.candy ? AppTheme.cocktail : AppTheme.candy;
            _images = data[_theme];
          });
        },
      ),
      body: ListView.builder(
          itemCount: _images.length,
          itemBuilder: (BuildContext context, int index) {
            return _getImage(_images[index], _dir);
          }),
    );
  }

  Widget _getImage(String name, String dir) {
    if (_theme != AppTheme.candy) {
      var file = _getLocalImageFile(name, dir);
      return Image.file(file);
    }
    return Image.asset('assets/images/$name');
  }

  File _getLocalImageFile(String name, String dir) => File('$dir/$name');

  Future<void> _downloadAssets(String name) async {
    if (_dir == null) {
      _dir = (await getApplicationDocumentsDirectory()).path;
    }

    if (!await _hasToDownloadAssets(name, _dir)) {
      return;
    }
    var zippedFile = await _downloadFile(
        '$api/$name.zip?alt=media&token=7442d067-a656-492f-9791-63e8fc082379',
        '$name.zip',
        _dir);

    var bytes = zippedFile.readAsBytesSync();
    var archive = ZipDecoder().decodeBytes(bytes);

    for (var file in archive) {
      var filename = '$_dir/${file.name}';
      if (file.isFile) {
        var outFile = File(filename);
        outFile = await outFile.create(recursive: true);
        await outFile.writeAsBytes(file.content);
      }
    }
  }

  Future<bool> _hasToDownloadAssets(String name, String dir) async {
    var file = File('$dir/$name.zip');
    return !(await file.exists());
  }

  Future<File> _downloadFile(String url, String filename, String dir) async {
    var req = await http.Client().get(Uri.parse(url));
    var file = File('$dir/$filename');
    return file.writeAsBytes(req.bodyBytes);
  }
}

Then you have to list all files and added them (logically to the corresponding theme)

data.dart:

import 'main.dart' show AppTheme;

const Map<AppTheme, List<String>> data = const {
  AppTheme.candy: [
    'art-background-blue-1289363.jpg',
    'assortment-bright-candy-1043519.jpg',
    'bright-candies-cherry-1405760.jpg',
    'bright-candies-colorful-539447.jpg',
    'bright-candy-chewy-1328885.jpg',
  ],
  AppTheme.cocktail: [
    'alcohol-alcoholic-beverage-beverage-1304540.jpg',
    'alcohol-alcoholic-beverage-beverage-1723638.jpg',
    'alcohol-black-background-close-up-800390.jpg',
    'alcoholic-beverage-beverage-cocktail-970197.jpg',
    'bar-beverage-blur-338713.jpg',
  ]
};

For more information check this Github project and Medium article

Share:
4,467
questionasker
Author by

questionasker

I'm Web, Unity3D &amp; Flutter Developer. I love to share my ideas at my web, please visit my website for any tutorial related to marketing, programming, docker, linux, etc

Updated on December 15, 2022

Comments

  • questionasker
    questionasker over 1 year

    currently, my apps have many assets (images, sound, font, json, SQL-lite database file, etc). All have defined in pubspec.yaml

    However, due to a request to reduce APK size, I need some of them to be downloaded when Apps be launched and save it to storage, so no need to download it next time.

    if assets are not yet ready, it should waiting a sec and show loading bar circle.

    The question is how to do this thing, Any Example?

  • Shady Mohamed Sherif
    Shady Mohamed Sherif over 4 years
    Welcome, if It worked for you please accept the answer :)
  • questionasker
    questionasker over 4 years
    Hi, I still confused on this code: for (var file in archive) { var filename = '$_dir/${file.name}'; if (file.isFile) { var outFile = File(filename); outFile = await outFile.create(recursive: true); await outFile.writeAsBytes(file.content); } } why we need it. how about if the zip file contain root folder?
  • Shady Mohamed Sherif
    Shady Mohamed Sherif over 4 years
    Welcome again, This code supports a zip file that contains a list of files directly. If you are going to and folders It will be more complicated. you can check the example in the archive package pub.dev/packages/archive#-example-tab- . If you did'y figure it out, It will try to write the code for you.
  • questionasker
    questionasker over 4 years
    I think i will use zip file contain no root folder. However, to verify my apps has to download the zip and extract it, i need to know the location of the assets file. I will use file manager apps to browse it on Android. Any idea?
  • Shady Mohamed Sherif
    Shady Mohamed Sherif over 4 years
    You can see applications files by normal file explorer. If you want to check the files by yourself not a code, first you have to print _dir var which contains (await getApplicationDocumentsDirectory()).path. then reach to that path using android studio device file explorer you will find on the right-down corner. Note that your app must be in debug mode.