首先,我是一个完整的初学者(不仅在dart / flutter中),而且在一般的编程方面。我目前正在使用聊天应用。
目标:当我在聊天屏幕中选择图像时,例如WhatsApp-它应立即显示为消息(在上载到Firestore时带有加载符号的占位符)。
问题:一切正常,但这是一个问题:当我通过图像选择器选择图像时,该图像被上传,但是在聊天中将其显示为消息之前会有一段时间。 imagepicker->写入Firestore-> Streambuilder (listView.builder)。
我的尝试:我正在使用CachedNetworkImage,但不幸的是,它一直在显示占位符(CircularProgressIndicator),而不是实际图像。如果我只是显示图像,则一切正常(除了延迟)。
此外,我非常感谢一些想法来使应用程序运行更快/改善代码;-)。
//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),
);
}
}
答案 0 :(得分:4)
它对我的情况有用。我使用了cached_network_image:^ 2.0.0
flutter clean
在模拟器上退出应用程序,重新运行该应用程序。
答案 1 :(得分:1)
我刚刚更改了'imageUrl:'和'placeholder:'的顺序,现在可以使用了。奇怪的行为。感谢您的帮助。
答案 2 :(得分:1)
对我来说,解决方案是将版本降级到
cached_network_image: ^1.1.1
并运行:
flutter clean
在最新版本中,即使按照另一个答案中的描述切换了顺序,它也根本不起作用。
答案 3 :(得分:1)
尝试:
placeholder: (context, url) => Container(child: CircularProgressIndicator()),
答案 4 :(得分:1)
您需要重新安装该应用。首次安装时热重载不起作用。
答案 5 :(得分:0)
指定对我有用的图像高度。 或者您可以尝试如下操作:
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),
)
答案 6 :(得分:0)
在搜索所有网站后为我工作:=>
1.为cached_network_image安装最新的依赖:
2.如果您正在调试或运行,请停止应用
3.Flutter 干净
4.完全来自IDE
5.重新打开您的 IDE 并运行 pub get
觉得有帮助就点个赞吧:)