我正在学习Flutter,并希望像内置的Widget
一样制作CircleAvatar
。但是,我希望行为
NetworkImage
)和缩写(即BB)以下代码有效,但在聊天演示中使用时,随着多个MyAvatars的添加而分崩离析。 在initState上进行断点显示始终使用输入的第一个消息文本调用它 - 而不是我所期望的。 它也像图像“重新加载”一样闪烁。似乎小部件正在以我不理解的方式重用。
class MyAvatar extends StatefulWidget {
NetworkImage image;
MyAvatar({this.text}) {
debugPrint("MyAvatar " + this.text);
if (text.contains('fun')) {
this.image = new NetworkImage("https://cdn3.iconfinder.com/data/icons/minicons-for-web-sites/24/minicons2-14-512.png");
}
}
final String text;
@override
MyAvatarState createState() {
return new MyAvatarState();
}
}
class MyAvatarState extends State<MyAvatar> {
bool showImage = false;
@override
initState() {
super.initState();
if (widget.image != null) {
var completer = widget.image.load(widget.image);
completer.addListener((info, sync) {
setState(() {
showImage = true;
});
});
}
}
@override
Widget build(BuildContext context) {
return !showImage ? new CircleAvatar(radius: 40.0, child: new Text(widget.text[0]))
: new CircleAvatar(radius: 40.0, backgroundImage: widget.image);
}
}
我仍然遇到麻烦 - 完整代码
import 'package:flutter/material.dart';
// Modify the ChatScreen class definition to extend StatefulWidget.
class ChatScreen extends StatefulWidget { //modified
ChatScreen() {
debugPrint("ChatScreen - called on hot reload");
}
@override //new
State createState() {
debugPrint("NOT on hot reload");
return new ChatScreenState();
} //new
}
// Add the ChatScreenState class definition in main.dart.
class ChatScreenState extends State<ChatScreen> {
final List<ChatMessage> _messages = <ChatMessage>[];
final TextEditingController _textController = new TextEditingController(); //new
ChatScreenState() {
debugPrint("ChatScreenState - not called on hot reload");
}
@override //new
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Friendlychat")),
body: new Column( //modified
children: <Widget>[ //new
new Flexible( //new
child: new ListView.builder( //new
padding: new EdgeInsets.all(8.0), //new
reverse: true, //new
itemBuilder: (_, int index) => _messages[index], //new
itemCount: _messages.length, //new
) //new
), //new
new Divider(height: 1.0), //new
new Container( //new
decoration: new BoxDecoration(
color: Theme.of(context).cardColor), //new
child: _buildTextComposer(), //modified
), //new
] //new
), //new
);
}
Widget _buildTextComposer() {
return new IconTheme(
data: new IconThemeData(color: Theme
.of(context)
.accentColor),
child:
new Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: new Row(
children: <Widget>[
new Container( //new
margin: new EdgeInsets.symmetric(horizontal: 4.0), //new
child: new IconButton( //new
icon: new Icon(Icons.send),
onPressed: () =>
_handleSubmitted(_textController.text)), //new
),
new Flexible(
child: new TextField(
controller: _textController,
onSubmitted: _handleSubmitted,
decoration: new InputDecoration.collapsed(
hintText: "Send a message"),
)
),
])
)
);
}
void _handleSubmitted(String text) {
_textController.clear();
ChatMessage message = new ChatMessage(text: text);
setState(() {
_messages.insert(0, message);
});
}
}
const String _name = "Hardcoded Name";
class ChatMessage extends StatelessWidget {
ChatMessage({this.text, this.image, this.useImage});
final String text;
final NetworkImage image;
final Map useImage;
@override
Widget build(BuildContext context) {
var use = true; //useImage != null && useImage['use'];
var image = new NetworkImage("https://cdn3.iconfinder.com/data/icons/minicons-for-web-sites/24/minicons2-14-512.png");
if (text.contains('bad')) {
image = new NetworkImage("https://cdn3.iconfinder.com/data/icons/minicons-for-web-sites/24/minicons2-14-512.pngz");
}
return new Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
margin: const EdgeInsets.only(right: 16.0),
child : new CustomCircleAvatar(initials: text[0], myImage: image)
),
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(_name, style: Theme.of(context).textTheme.subhead),
new Container(
margin: const EdgeInsets.only(top: 5.0),
child: new Text(text),
),
],
),
],
),
);
}
}
class CustomCircleAvatar extends StatefulWidget {
NetworkImage myImage;
String initials;
CustomCircleAvatar({this.myImage, this.initials}) {
debugPrint(initials);
}
@override
_CustomCircleAvatarState createState() => new _CustomCircleAvatarState();
}
class _CustomCircleAvatarState extends State<CustomCircleAvatar>{
bool _checkLoading = true;
@override
void initState() {
if (widget.myImage != null) {
widget.myImage.resolve(new ImageConfiguration()).addListener((image, sync) {
if (mounted && image != null) {
setState(() {
_checkLoading = false;
});
}
});
}
}
@override
Widget build(BuildContext context) {
return _checkLoading == true ? new CircleAvatar(child: new Text(widget.initials))
: new CircleAvatar(backgroundImage: widget.myImage);
}
}
输入'fun'作为消息,然后'bad'作为第二个 - image 我们的想法是,根据您输入的内容,可能会加载(或不加载)不同的图像。在“无法加载”的情况下,首字母应该保留。
答案 0 :(得分:3)
您可以通过向ImageStream
添加一个可以从ImageConfiguration
获取的侦听器来实现此功能,
在这里,我将相同的数据提供给我的ListView
您当然可以通过在任何课程中添加List
图像和缩写作为字段来自定义,然后使用ListView.builder
而是能够通过索引循环它们。
class CustomCircleAvatar extends StatefulWidget {
NetworkImage myImage;
String initials;
CustomCircleAvatar({this.myImage, this.initials});
@override
_CustomCircleAvatarState createState() => new _CustomCircleAvatarState();
}
class _CustomCircleAvatarState extends State<CustomCircleAvatar>{
bool _checkLoading = true;
@override
void initState() {
widget.myImage.resolve(new ImageConfiguration()).addListener((_, __) {
if (mounted) {
setState(() {
_checkLoading = false;
});
}
});
}
@override
Widget build(BuildContext context) {
return _checkLoading == true ? new CircleAvatar(
child: new Text(widget.initials)) : new CircleAvatar(
backgroundImage: widget.myImage,);
}
}
现在您可以像这样使用它:
void main() {
runApp(new MaterialApp (home: new MyApp()));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("Custom Circle Avatar"),),
body: new ListView(children: new List.generate(20, (int index) {
return new Container(
height: 100.0,
width: 100.0,
child: new CustomCircleAvatar(myImage: new NetworkImage(
"https://www.doginni.cz/front_path/images/dog_circle.png"),
initials: "Dog",
),
);
}),),
);
}
}
答案 1 :(得分:1)
这确实非常容易。使用Usage: OCPlatform11.3.2_21REL [-f <path_to_installer_properties_file> | -options]
(to execute the installer)
where options include:
-?
show this help text
-i [swing | console | silent]
specify the user interface mode for the installer
-D<name>=<value>
specify installer properties
-r <path_to_generate_response_file>
Generates response file.
并构建适当的CachetNetworkImage
。
CircleAvatar
答案 2 :(得分:0)
@aziza的答案确实是我一段时间以来才能找到的唯一答案,并且花了我一段时间才能阅读并理解。我尝试实施它,尽管最终确实使它起作用,但仍然存在一些问题。我认为我有一个更具可读性的(至少对我来说是这样!)/最新的答案,这可能有助于某人绊倒这个问题:
class FallBackAvatar extends StatefulWidget {
final AssetImage image;
final String initials;
final TextStyle textStyle;
final Color circleBackground;
FallBackAvatar({@required this.image, @required this.initials, @required this.circleBackground, @required this.textStyle});
@override
_FallBackAvatarState createState() => _FallBackAvatarState();
}
class _FallBackAvatarState extends State<FallBackAvatar> {
bool _checkLoading = true;
@override
initState() {
super.initState();
// Add listeners to this class
ImageStreamListener listener = ImageStreamListener(_setImage, onError: _setError);
widget.image.resolve(ImageConfiguration()).addListener(listener);
}
void _setImage(ImageInfo image, bool sync) {
setState(() => _checkLoading = false);
//DO NOT DISPOSE IF IT WILL REBUILD (e.g. Sliver/Builder ListView)
dispose();
}
void _setError(dynamic dyn, StackTrace st) {
setState(() => _checkLoading = true);
dispose();
}
@override
Widget build(BuildContext context) {
return _checkLoading == true ? new CircleAvatar(
backgroundColor: widget.circleBackground,
child: new Text(widget.initials, style: widget.textStyle)) : new CircleAvatar(
backgroundImage: widget.image,
backgroundColor: widget.circleBackground,);
}
}
我要手动处理两点,因为我知道在此之后应该不再进行重建(您是否获得了图像?好!除非您是条子或其他东西,否则图像就不会重建)加载?那么就是这样-不再进行重建)。这也可以处理由于某些原因而没有出现AssetImage(在我的情况下是其Asset图像,但您可以使用任何类型的图像提供程序)的错误情况。
第二次编辑,因为我个人的问题最好不在此答案之列。因此,我注意到加载个人资料图像时稍有延迟(例如一秒钟)。但是随后图像泛滥成灾。不喜欢这种过渡,所以这里是一个带有AnimatedSwitcher的过渡:
class FallBackAvatar extends StatefulWidget {
final AssetImage image;
final String initials;
final TextStyle textStyle;
final Color circleBackground;
final double radius;
final int msAnimationDuration;
FallBackAvatar({@required this.image, @required this.initials, @required this.circleBackground, @required this.textStyle, @required this.radius, this.msAnimationDuration});
@override
_FallBackAvatarState createState() => _FallBackAvatarState();
}
class _FallBackAvatarState extends State<FallBackAvatar> {
bool _imgSuccess = false;
@override
initState() {
super.initState();
// Add listeners to this class
ImageStreamListener listener = ImageStreamListener(_setImage, onError: _setError);
widget.image.resolve(ImageConfiguration()).addListener(listener);
}
void _setImage(ImageInfo image, bool sync) {
setState(() => _imgSuccess = true);
}
void _setError(dynamic dyn, StackTrace st) {
setState(() => _imgSuccess = false);
dispose();
}
Widget _fallBackAvatar() {
return Container(
height: widget.radius*2,
width: widget.radius*2,
decoration: BoxDecoration(
color: widget.circleBackground,
borderRadius: BorderRadius.all(Radius.circular(widget.radius))
),
child: Center(child: Text(widget.initials, style: widget.textStyle))
);
}
Widget _avatarImage() {
return CircleAvatar(
backgroundImage: widget.image,
backgroundColor: widget.circleBackground
);
}
@override
Widget build(BuildContext context) {
return AnimatedSwitcher(
duration: Duration(milliseconds: widget.msAnimationDuration ?? 500),
child: _imgSuccess ? _avatarImage() : _fallBackAvatar(),
);
}
}
答案 3 :(得分:0)
这里是具有堆叠体系结构的示例,其中回退是人员图标。
ViewBuilder
和ViewModel
只是堆叠体系结构替代中的扩展小部件。 @swidget
是功能性小部件。您可以通过StatefulWidget
实现相同的功能。
@swidget
Widget avatarView({String userId, double radius = 24}) =>
ViewBuilder<AvatarViewModel>(
viewModelBuilder: () => AvatarViewModel(),
builder: (model) => CircleAvatar(
radius: radius,
backgroundColor: CColors.blackThird,
backgroundImage: NetworkImage(
Config.photoUrl + userId ?? userService.id,
),
child: model.isFailed ? Icon(EvaIcons.person, size: radius) : null,
onBackgroundImageError: (e, _) => model.isFailed = e != null,
),
);
class AvatarViewModel extends ViewModel {
bool _isFailed = false;
bool get isFailed => _isFailed;
set isFailed(bool isFailed) {
_isFailed = isFailed;
notifyListeners();
}
}
答案 4 :(得分:0)
其实代码可以更简单:
如果您想在图像不可用时放置文本,您只需使用 foregroundImage 而不是 backgroundImage。
默认会显示文字,图片加载时会覆盖文字,无需处理图片加载状态等
如果您需要知道图像是否有错误,您可以使用 onForegroundImageError 拦截它。
示例函数:
Widget CircleAvatarTest(
{String? imageUrl,
String? text,
double radius = 35,
Color? backgroundColor}) {
return CircleAvatar(
radius: radius,
child: (text != null)
? Center(
child: Text(text,
style: TextStyle(
color: Colors.white,
fontSize: radius * 2 / text.length - 10,
)),
)
: null,
foregroundImage: imageUrl == null ? null : NetworkImage(imageUrl),
backgroundColor: backgroundColor,
//onForegroundImageError: (e,trace){/*....*/},
);
}