Line data Source code
1 : import 'dart:io';
2 : import 'dart:math';
3 :
4 : import 'package:flutter/material.dart';
5 : import 'package:provider/provider.dart';
6 :
7 : import 'package:cwtch/cwtch_icons_icons.dart';
8 : import 'package:cwtch/models/contact.dart';
9 : import 'package:cwtch/models/profile.dart';
10 : import 'package:cwtch/themes/opaque.dart';
11 :
12 : import '../settings.dart';
13 :
14 : class ProfileImage extends StatefulWidget {
15 1 : ProfileImage({
16 : required this.imagePath,
17 : required this.diameter,
18 : required this.border,
19 : this.badgeCount = 0,
20 : required this.badgeColor,
21 : required this.badgeTextColor,
22 : this.maskOut = false,
23 : this.tooltip = "",
24 : this.disabled = false,
25 : this.badgeEdit = false,
26 : this.badgeIcon,
27 : this.defaultImagePath = "",
28 : this.managed = false,
29 : });
30 : final double diameter;
31 : final String imagePath;
32 : final String defaultImagePath;
33 : final Color border;
34 : final int badgeCount;
35 : final Color badgeColor;
36 : final Color badgeTextColor;
37 : final bool maskOut;
38 : final bool disabled;
39 : final bool badgeEdit;
40 : final String tooltip;
41 : final Widget? badgeIcon;
42 : final bool managed;
43 :
44 1 : @override
45 1 : _ProfileImageState createState() => _ProfileImageState();
46 :
47 0 : static ProfileImage fromContact(Settings settings, ContactInfoState contact, double diam) {
48 0 : return ProfileImage(
49 0 : imagePath: contact.imagePath,
50 0 : defaultImagePath: contact.defaultImagePath,
51 : diameter: diam,
52 0 : border: contact.getBorderColor(settings.theme),
53 0 : badgeColor: settings.theme.portraitContactBadgeColor,
54 0 : badgeTextColor: settings.theme.portraitContactBadgeTextColor,
55 : );
56 : }
57 :
58 0 : static ProfileImage fromProfile(Settings settings, ProfileInfoState profile, double diam) {
59 0 : return ProfileImage(
60 0 : imagePath: profile.imagePath,
61 0 : defaultImagePath: profile.defaultImagePath,
62 : diameter: diam,
63 0 : border: profile.getBorderColor(settings.theme),
64 0 : badgeColor: settings.theme.portraitContactBadgeColor,
65 0 : badgeTextColor: settings.theme.portraitContactBadgeTextColor,
66 : );
67 : }
68 : }
69 :
70 : class _ProfileImageState extends State<ProfileImage> {
71 1 : @override
72 : Widget build(BuildContext context) {
73 2 : var path = widget.imagePath;
74 : // preserve original behaviour where callees checked ImagePreviewsExperiment themselves
75 5 : if (!Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment) && !widget.defaultImagePath.isEmpty) {
76 0 : path = widget.defaultImagePath;
77 : }
78 :
79 3 : var file = new File(widget.imagePath);
80 1 : var image = Image.file(
81 : file,
82 4 : cacheWidth: (4 * widget.diameter.floor()),
83 : filterQuality: FilterQuality.medium,
84 : fit: BoxFit.cover,
85 : alignment: Alignment.center,
86 : // We need some theme specific blending here...we might want to consider making this a theme level attribute
87 2 : colorBlendMode: !widget.maskOut
88 4 : ? Provider.of<Settings>(context).theme.mode == mode_dark
89 : ? BlendMode.softLight
90 : : BlendMode.darken
91 : : BlendMode.srcOut,
92 3 : color: Provider.of<Settings>(context).theme.portraitBackgroundColor,
93 : isAntiAlias: false,
94 2 : width: widget.diameter,
95 2 : height: widget.diameter,
96 0 : errorBuilder: (context, error, stackTrace) {
97 : // on android the above will fail for asset images, in which case try to load them the original way
98 0 : return Image.asset(
99 0 : widget.imagePath,
100 : filterQuality: FilterQuality.medium,
101 : // We need some theme specific blending here...we might want to consider making this a theme level attribute
102 0 : colorBlendMode: !widget.maskOut
103 0 : ? Provider.of<Settings>(context).theme.mode == mode_dark
104 : ? BlendMode.softLight
105 : : BlendMode.darken
106 : : BlendMode.srcOut,
107 0 : color: Provider.of<Settings>(context).theme.portraitBackgroundColor,
108 : isAntiAlias: true,
109 0 : width: widget.diameter,
110 0 : height: widget.diameter,
111 : );
112 : },
113 : );
114 :
115 1 : return RepaintBoundary(
116 1 : child: Stack(
117 1 : children: [
118 2 : widget.managed
119 0 : ? ClipRRect(
120 0 : borderRadius: BorderRadiusGeometry.all(Radius.circular(8)),
121 : clipBehavior: Clip.antiAlias,
122 0 : child: Container(
123 0 : width: widget.diameter,
124 0 : height: widget.diameter,
125 0 : color: widget.border,
126 0 : foregroundDecoration: widget.disabled
127 0 : ? BoxDecoration(
128 0 : color: Provider.of<Settings>(context).theme.portraitBackgroundColor, //Colors.grey,
129 : backgroundBlendMode: BlendMode.color, //saturation,
130 : )
131 : : null,
132 0 : child: Padding(
133 : padding: const EdgeInsets.all(2.0), //border size
134 0 : child: ClipRRect(
135 0 : borderRadius: BorderRadiusGeometry.all(Radius.circular(7)),
136 : clipBehavior: Clip.antiAlias,
137 0 : child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image),
138 : ),
139 : ),
140 : ),
141 : )
142 1 : : ClipOval(
143 : clipBehavior: Clip.antiAlias,
144 1 : child: Container(
145 2 : width: widget.diameter,
146 2 : height: widget.diameter,
147 2 : color: widget.border,
148 2 : foregroundDecoration: widget.disabled
149 0 : ? BoxDecoration(
150 0 : color: Provider.of<Settings>(context).theme.portraitBackgroundColor, //Colors.grey,
151 : backgroundBlendMode: BlendMode.color, //saturation,
152 : )
153 : : null,
154 1 : child: Padding(
155 : padding: const EdgeInsets.all(2.0), //border size
156 1 : child: ClipOval(
157 : clipBehavior: Clip.antiAlias,
158 3 : child: widget.tooltip == "" ? image : Tooltip(message: widget.tooltip, child: image),
159 : ),
160 : ),
161 : ),
162 : ),
163 : // badge
164 1 : Visibility(
165 7 : visible: widget.badgeIcon != null || widget.badgeEdit || widget.badgeCount > 0,
166 1 : child: Positioned(
167 : bottom: 0.0,
168 : right: 0.0,
169 1 : child: CircleAvatar(
170 4 : radius: max(10.0, widget.diameter / 6.0),
171 2 : backgroundColor: widget.badgeColor,
172 2 : child: widget.badgeEdit
173 0 : ? Icon(Icons.edit, color: widget.badgeTextColor)
174 2 : : (widget.badgeIcon != null
175 0 : ? widget.badgeIcon
176 10 : : Text(widget.badgeCount > 99 ? "99+" : widget.badgeCount.toString(), style: TextStyle(color: widget.badgeTextColor, fontSize: 8.0))),
177 : ),
178 : ),
179 : ),
180 : // disabled center icon
181 1 : Visibility(
182 2 : visible: widget.disabled,
183 1 : child: Container(
184 2 : width: widget.diameter,
185 2 : height: widget.diameter,
186 1 : child: Center(
187 7 : child: Icon(CwtchIcons.negative_heart_24px, size: widget.diameter / 1.5, color: Provider.of<Settings>(context).theme.portraitOfflineBorderColor),
188 : ),
189 : ),
190 : ),
191 : ],
192 : ),
193 : );
194 : }
195 : }
|