CachedNetworkImage only shows placeholder and doesn't load image in Flutter chat app

Viewed 12195

First of all, I am a complete beginner (not only in dart/flutter), but in programming in general. I am currently working on a chat app.

Goal: When I am picking an image within the ChatScreen - analog to e.g. WhatsApp - it should immediately appear as a message (placeholder with loading symbol, while it is uploaded to Firestore).

Problem: Everything is working fine, but here is one issue: When I am picking an image via the imagepicker, it get's uploaded but there's a delay until it is displayed as a message in the chat. imagepicker -> write to Firestore -> Streambuilder (listView.builder).

My attempt: I am using CachedNetworkImage, but unfortunately, it keep's showing the placeholder (CircularProgressIndicator), but not the actual image. If I am just displaying the image, everything works fine (besides the delay).

Moreover, I would highly appreciate some ideas to make the app run faster / improve the code ;-).

//Chat screen which lists all the chat messages, including _handleSubmitted and the _buildTextComposer

import 'dart:math';
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:intl/intl.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:transparent_image/transparent_image.dart';
import 'package:flutter_image/network.dart';
import 'package:flutter_native_image/flutter_native_image.dart';

final database = Firestore.instance
    .collection('nachrichten')
    .document('G5xlQHvb56ZqpWs7ojUV');
final reference = FirebaseDatabase.instance.reference().child('messages');

class ChatScreen extends StatefulWidget {
  @override
  State createState() => new ChatScreenState();
}

class ChatScreenState extends State<ChatScreen> with TickerProviderStateMixin {
  final TextEditingController _textController = new TextEditingController();
  final ScrollController _scrollController = new ScrollController();

  bool _isComposing = false;
  bool isLoading;
  String imageUrl;
  File imageFile;

  //optional from other code
  @override
  void initState() {
    super.initState();
    imageUrl = '';
  }

  Future getImage() async {
    imageFile = await ImagePicker.pickImage(source: ImageSource.camera);

    if (imageFile != null) {
      setState(() {
        isLoading = true;
      });
      uploadFile();
    }
  }

  Future uploadFile() async {
    //file compression
    File compressedFile = await FlutterNativeImage.compressImage(imageFile.path,
        quality: 100, percentage: 100);
    String fileName = DateTime.now().millisecondsSinceEpoch.toString();
    StorageReference reference = FirebaseStorage.instance.ref().child(fileName);
    StorageUploadTask uploadTask = reference.putFile(compressedFile);
    StorageTaskSnapshot storageTaskSnapshot = await uploadTask.onComplete;
    storageTaskSnapshot.ref.getDownloadURL().then((downloadUrl) {
      imageUrl = downloadUrl;
      setState(() {
        isLoading = false;
        _handleSubmitted(imageUrl: imageUrl);
      });
    }, onError: (err) {
      setState(() {
        isLoading = false;
      });
      Fluttertoast.showToast(msg: 'This file is not an image');
    });
  }

  //Builds the button text composer, including camera icon, text input and send button
  Widget _buildTextComposer() {
    return new IconTheme(
      data: new IconThemeData(color: Theme.of(context).accentColor),
      child: new Container(
          margin: const EdgeInsets.symmetric(horizontal: 0.80),
          child: new Row(children: <Widget>[
            new Container(
              margin: new EdgeInsets.symmetric(horizontal: 0.4),
              child: new IconButton(
                icon: new Icon(Icons.photo_camera),
                onPressed: getImage,
              ),
            ),
            new Flexible(
              child: Container(
                margin: const EdgeInsets.symmetric(vertical: 10.0),
                padding: EdgeInsets.all(10.0),
                decoration: new BoxDecoration(
                  border: Border.all(color: Colors.grey.shade200),
                  borderRadius: new BorderRadius.circular(20.0),
                ),

                //container with constraint limits the maximum height of the text input field
                child: new Container(
                  constraints: BoxConstraints.loose(Size.fromHeight(100.0)),
                  child: new TextField(
                    maxLines: null,
                    keyboardType: TextInputType.multiline,
                    controller: _textController,
                    onChanged: (String text) {
                      setState(() {
                        _isComposing = text.length > 0;
                      });
                    },
//                    onSubmitted: _handleSubmitted,
                    decoration: new InputDecoration.collapsed(
                        hintText: "Nachricht schreiben..."),
                  ),
                ),
              ),
            ),
            new Container(
                margin: new EdgeInsets.symmetric(horizontal: 0.4),
                child: Theme.of(context).platform == TargetPlatform.iOS
                    ? new CupertinoButton(
                        child: new Text("Send"),
                        onPressed: _isComposing
                            ? () => _handleSubmitted(text: _textController.text)
                            : null,
                      )
                    : new IconButton(
                        icon: new Icon(Icons.send),
                        onPressed: _isComposing
                            ? () => _handleSubmitted(text: _textController.text)
                            : null,
                      )),
          ]),
          decoration: Theme.of(context).platform == TargetPlatform.iOS
              ? new BoxDecoration(
                  border:
                      new Border(top: new BorderSide(color: Colors.grey[200])))
              : null),
    );
  }

  //Builds the actual chat screen with Scaffold
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
          title: new Text("Chat.here Gruppenchat"),
          centerTitle: true,
          elevation:
              Theme.of(context).platform == TargetPlatform.iOS ? 0.0 : 4.0),
      body: StreamBuilder<QuerySnapshot>(
          stream: Firestore.instance
              .collection('nachrichten')
              .orderBy('timestamp', descending: true)
              .limit(20)
              .snapshots(),
          builder:
              (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
            if (!snapshot.hasData) return Text('Loading data');
            final int documentsLength = snapshot.data.documents.length;
            return Container(
                child: Column(
                  children: <Widget>[
                    new Flexible(
                        child: new ListView.builder(
                      controller: _scrollController,
                      reverse: true,
                      itemCount: documentsLength,
                      itemBuilder: (context, int index) {
                        final DocumentSnapshot document =
                            snapshot.data.documents[index];
                        return new Container(
                          margin: const EdgeInsets.symmetric(
                              horizontal: 10.0, vertical: 10.0),
                          child: new Row(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: <Widget>[
                              new Container(
                                margin: const EdgeInsets.only(right: 16.0),
                                child: new CircleAvatar(
                                    child: new Text(
                                        document['author'].substring(0, 1))),
                              ),
                              new Expanded(
                                child: new Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: <Widget>[
                                    new Row(
                                      children: <Widget>[
                                        new Text(document['author'],
                                            style: TextStyle(
                                                fontSize: 12.0,
                                                color: Colors.black45,
                                                fontWeight: FontWeight.bold)),
                                        new Text(
                                            ' ' +
                                                DateFormat("MMM. d. '|' HH:mm")
                                                    .format(
                                                        document['timestamp']),
                                            style: TextStyle(
                                                fontSize: 12.0,
                                                color: Colors.black45))
                                      ],
                                    ),

                                    //can be deleted. just to test the picture.
                                    (document['text'] == null)
                                        ? new Container(
                                            child: new ClipRRect(
                                              borderRadius:
                                                  new BorderRadius.circular(
                                                      7.0),
                                              child: CachedNetworkImage(
                                                placeholder: Container(
                                                  child:
                                                      CircularProgressIndicator(
                                                    valueColor:
                                                        AlwaysStoppedAnimation<
                                                                Color>(
                                                            Colors.orange),
                                                  ),
                                                  width: 200.0,
                                                  height: 200.0,
                                                  padding: EdgeInsets.all(70.0),
                                                  decoration: BoxDecoration(
                                                    color: Colors.blue,
                                                    borderRadius:
                                                        BorderRadius.all(
                                                      Radius.circular(8.0),
                                                    ),
                                                  ),
                                                ),
                                                imageUrl:
                                                    'https://wuppertal-total.de/wp-content/uploads/2019/01/stamp_schmidt_fdp.jpg',
                                                width: 200.0,
                                                height: 200.0,
                                                fit: BoxFit.cover,
                                              ),


                                            ),
                                            margin:
                                                EdgeInsets.only(right: 50.0),
                                          )

                                        : new Card(
                                            margin:
                                                EdgeInsets.only(right: 50.0),
                                            //green color for messages of yourself
                                            color:
                                                document['author'] == "Matthias"
                                                    ? Color.fromRGBO(
                                                        220, 255, 202, 1.0)
                                                    : null,
                                            child: new Container(
                                                padding: EdgeInsets.all(6.0),
                                                child: new Text(
                                                  document['text'],
                                                  style:
                                                      TextStyle(fontSize: 15.0),
                                                )),
                                            shape: RoundedRectangleBorder(
                                                borderRadius:
                                                    BorderRadius.circular(7.0)),
                                          ),

                                  ],
                                ),
                              ),
                            ],
                          ),
                        );
                      },
                    )),
                    new Divider(height: 1.0),
                    new Container(
                      decoration:
                          new BoxDecoration(color: Theme.of(context).cardColor),
                      child: _buildTextComposer(),
                    ),
                  ],
                ),
                decoration: Theme.of(context).platform == TargetPlatform.iOS
                    ? new BoxDecoration(
                        border: new Border(
                            top: new BorderSide(color: Colors.grey[200])))
                    : null);
          }),
    );
  }


  void _handleSubmitted({String text, String imageUrl}) {
    _textController.clear();
    setState(() {
      _isComposing = false;
    });

    //creation of an own document in Firestore
    Firestore.instance.runTransaction((Transaction transaction) async {
      CollectionReference reference =
          Firestore.instance.collection('nachrichten');

      await reference.add({
        "text": text,
        "author": "Matthias",
        "imageUrl": imageUrl,
        "timestamp": DateTime.now(),

        //"timestamp": FieldValue.serverTimestamp()
      });
    });

//Let the chat list jump to the newest chat message at the bottom
    _scrollController.animateTo(
      0.0,
      curve: Curves.easeOut,
      duration: const Duration(milliseconds: 300),
    );
  }

}
8 Answers

it worked for my case. I used cached_network_image: ^2.0.0

flutter clean

Quit app on emulator, re-run the app.

For me the solution was to downgrade the version to

cached_network_image: ^1.1.1

and run:

flutter clean

In the newest version it didn't work at all, even after switching the order as described in the other answer.

You need to reinstall the app. Hot reloading not working at first installing.

Specify image height it works for me. Or u can try like below:

   CachedNetworkImage(
   imageUrl: "your image url",
   fit: BoxFit.cover,
   width: MediaQuery.of(context).size.width,
   placeholder: (context,url) => CircularProgressIndicator(),
   errorWidget: (context,url,error) => new Icon(Icons.error),
 )

Try:

placeholder: (context, url) => Container(child: CircularProgressIndicator()),

I just changed the order of 'imageUrl:' and 'placeholder:' and now it's working. Strange behaviour. Thanks for your help.

Worked for me after searching all over the sites:=>

1.Install the latest dependency for cached_network_image:

2.Stop the app if you are debugging or running

3.Flutter clean

4.Quite from IDE

5.Reopen your IDE and run pub get

  1. Run the app/web/...

Give it a like if you found it helpful :)

Some possible methods:

  1. Restart app instead of hot-reload

  2. check the image URL itself

  3. check the cachedKey value to avoid conflicts

  4. flutter clean than flutter pub get

Related