Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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),
    );
  }

}
like image 642
user3532201 Avatar asked Jan 10 '19 15:01

user3532201


2 Answers

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

flutter clean

Quit app on emulator, re-run the app.

like image 101
Mr Special Avatar answered Oct 19 '22 21:10

Mr Special


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.

like image 3
ejdrian313 Avatar answered Oct 19 '22 21:10

ejdrian313