Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter pre-cache images

I'm working on a Flutter project where I want to pre-cache images at the start of the app. The idea is when you start the app the first time, it downloads a list of images either cache / stored in DB / stored in local storage / or any other viable solution. I don't really know the best practice here. And then when you start the app the next time you already have the photos so you don't want to download them again (based on the version of the backend data). From what I saw;

  • The cache, I don't really know if it is not persistent enough and I don't know if I'll have enough control over it.
  • The local storage, I think I'll have to ask the user permission to access the files of the device
  • The database, I'll have to encode/decode the photos every time I want to save/get them so it'll take some computation.

My ideal choice would be the database as I'd have control over the data and it's a rather small app so the computation is minimal and I won't have to ask the user permission.

I've tried over the last days to implement this using every of the solution stated above and I can't make it work.

Right now I want to store an Image into the database (I'm using sqflite) without displaying it and then read it to display it as a Widget from another Screen. I have 2 screens, the first one that I called SplashScreen to fetch and save the images and the second one which is HomeScreen to read the images from the database and display them.

SplashScreen.dart:

class SplashScreen extends StatefulWidget {
  @override
  _SplashScreenState createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin {
  bool hasInternet = true;
  var subscription;
  double loading = 0;

  @override
  initState() {
    super.initState();
    getPhotos();
    subscription = Connectivity().onConnectivityChanged.listen((ConnectivityResult connectivityResult) {
      setState(() {
        hasInternet = connectivityResult == ConnectivityResult.mobile || connectivityResult == ConnectivityResult.wifi;
      });
    });
  }
  dispose() {
    super.dispose();
    subscription.cancel();
  }

  Future getPhotos() async {
    List<String> photoUrls = await fetchPhotos();
    photoUrls.asMap().forEach((index, photoUrl) async {
      var response = await http
        .get(photoUrl);
      loading = index / photoUrls.length;
      // Convert Photo response and save them in DB
      imageDBFormat = ...
      savePhotosInDB(imageDBFormat)
    });
  }

 

  @override
  Widget build(BuildContext context) {
    if(loading == 1) {
      Navigator.pushNamed(context, '/');
    }

    return Center(
      child: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: AssetImage("assets/back.png"), 
            fit: BoxFit.cover,
          )
        ),
        child: Scaffold(
          backgroundColor: Theme.of(context).backgroundColor,
          body: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Container(
                    width: 300,
                    child: LinearProgressIndicator(
                      value: loading,
                      valueColor: AlwaysStoppedAnimation<Color>(ProjectColors.primaryDark),
                    ),
                  )
                ],
              ),
            ],
          )
        ),
      ),
    );
  }
}

HomeScreen.dart:

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateMixin {
  List<Widget> images;

  initState() {
    super.initState();
    getImages();
  }

  void getImages() async {
    List imgs = getImagesFromDB();

    setState(() {
      images = imgs.map((image) {
        // Convert imgs from db into Widget
        Widget imageWidget = ...
        return Container(
          child: imageWidget,
        );
      }).toList();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        image: DecorationImage(
          image: AssetImage("assets/back.png"), 
          fit: BoxFit.cover,
        )
      ),
      child: Scaffold(
        backgroundColor: Theme.of(context).backgroundColor,
        body: Column(
          children: images == null ? images : <Widget>[],
        ),
      )
    );
  }
}

I'm OK to reconsider any point to follow the best practices.

Thanks a lot for your help.

like image 704
Antoine ALEJANDRO Avatar asked Mar 31 '26 20:03

Antoine ALEJANDRO


1 Answers

You can use https://pub.dev/packages/cached_network_image library,it's using sqlite as a storage already and you can configure duration of persistance.

class CustomCacheManager extends BaseCacheManager {
  static const key = "customCache";

  static CustomCacheManager _instance;

  factory CustomCacheManager() {
    if (_instance == null) {
      _instance = new CustomCacheManager._();
    }
    return _instance;
  }

  CustomCacheManager._() : super(key,
      maxAgeCacheObject: Duration(months: 6),
      maxNrOfCacheObjects: 100);

  Future<String> getFilePath() async {
    var directory = await getTemporaryDirectory();
    return p.join(directory.path, key);
  }
}

and after that you can use in your build method:

CachedNetworkImage(
        imageUrl: "http://via.placeholder.com/350x150",
        placeholder: (context, url) => CircularProgressIndicator(),
        errorWidget: (context, url, error) => Icon(Icons.error),
        cacheManager: CustomCacheManager()
     ),
like image 110
dubace Avatar answered Apr 03 '26 11:04

dubace