| 1 | import 'package:cake_wallet/utils/image_utill.dart'; |
| 2 | import 'package:flutter/material.dart'; |
| 3 | |
| 4 | class ProviderOptionTile extends StatelessWidget { |
| 5 | const ProviderOptionTile({ |
| 6 | required this.onPressed, |
| 7 | required this.lightImagePath, |
| 8 | required this.darkImagePath, |
| 9 | required this.title, |
| 10 | this.topLeftSubTitle, |
| 11 | this.topRightSubTitle, |
| 12 | this.bottomLeftSubTitle, |
| 13 | this.bottomRightSubTitle, |
| 14 | this.leftSubTitleIconPath, |
| 15 | this.rightSubTitleLightIconPath, |
| 16 | this.rightSubTitleDarkIconPath, |
| 17 | this.description, |
| 18 | this.badges, |
| 19 | this.borderRadius, |
| 20 | this.imageHeight, |
| 21 | this.imageWidth, |
| 22 | this.padding, |
| 23 | this.titleTextStyle, |
| 24 | this.firstSubTitleTextStyle, |
| 25 | this.secondSubTitleTextStyle, |
| 26 | this.leadingIcon, |
| 27 | this.selectedBackgroundColor, |
| 28 | this.isSelected = false, |
| 29 | required this.isLightMode, |
| 30 | }); |
| 31 | |
| 32 | final VoidCallback onPressed; |
| 33 | final String lightImagePath; |
| 34 | final String darkImagePath; |
| 35 | final String title; |
| 36 | final String? topLeftSubTitle; |
| 37 | final String? topRightSubTitle; |
| 38 | final String? bottomLeftSubTitle; |
| 39 | final String? bottomRightSubTitle; |
| 40 | final String? leftSubTitleIconPath; |
| 41 | final String? rightSubTitleLightIconPath; |
| 42 | final String? rightSubTitleDarkIconPath; |
| 43 | final String? description; |
| 44 | final List<String>? badges; |
| 45 | final double? borderRadius; |
| 46 | final double? imageHeight; |
| 47 | final double? imageWidth; |
| 48 | final EdgeInsets? padding; |
| 49 | final TextStyle? titleTextStyle; |
| 50 | final TextStyle? firstSubTitleTextStyle; |
| 51 | final TextStyle? secondSubTitleTextStyle; |
| 52 | final IconData? leadingIcon; |
| 53 | final Color? selectedBackgroundColor; |
| 54 | final bool isSelected; |
| 55 | final bool isLightMode; |
| 56 | |
| 57 | @override |
| 58 | Widget build(BuildContext context) { |
| 59 | final backgroundColor = isSelected |
| 60 | ? Theme.of(context).colorScheme.primary |
| 61 | : Theme.of(context).colorScheme.surfaceContainer; |
| 62 | |
| 63 | final textColor = isSelected |
| 64 | ? Theme.of(context).colorScheme.onPrimary |
| 65 | : Theme.of(context).colorScheme.onSurface; |
| 66 | |
| 67 | final badgeColor = isSelected |
| 68 | ? Theme.of(context).colorScheme.surfaceContainer |
| 69 | : Theme.of(context).colorScheme.onSurface; |
| 70 | |
| 71 | final badgeTextColor = isSelected |
| 72 | ? Theme.of(context).colorScheme.onSurface |
| 73 | : Theme.of(context).colorScheme.surfaceContainer; |
| 74 | |
| 75 | final imagePath = isSelected |
| 76 | ? !isLightMode |
| 77 | ? darkImagePath |
| 78 | : lightImagePath |
| 79 | : !isLightMode |
| 80 | ? lightImagePath |
| 81 | : darkImagePath; |
| 82 | |
| 83 | final rightSubTitleIconPath = isSelected |
| 84 | ? !isLightMode |
| 85 | ? rightSubTitleDarkIconPath |
| 86 | : rightSubTitleLightIconPath |
| 87 | : !isLightMode |
| 88 | ? rightSubTitleLightIconPath |
| 89 | : rightSubTitleDarkIconPath; |
| 90 | |
| 91 | return GestureDetector( |
| 92 | onTap: onPressed, |
| 93 | child: Container( |
| 94 | width: double.infinity, |
| 95 | alignment: Alignment.center, |
| 96 | decoration: BoxDecoration( |
| 97 | borderRadius: BorderRadius.all(Radius.circular(borderRadius ?? 12)), |
| 98 | border: isSelected && !isLightMode ? Border.all(color: textColor) : null, |
| 99 | color: backgroundColor, |
| 100 | ), |
| 101 | child: Padding( |
| 102 | padding: padding ?? const EdgeInsets.all(16), |
| 103 | child: Column( |
| 104 | crossAxisAlignment: CrossAxisAlignment.center, |
| 105 | children: [ |
| 106 | Row( |
| 107 | children: [ |
| 108 | ImageUtil.getImageFromPath( |
| 109 | imagePath: imagePath, height: imageHeight, width: imageWidth), |
| 110 | SizedBox(width: 8), |
| 111 | Expanded( |
| 112 | child: Container( |
| 113 | child: Row( |
| 114 | children: [ |
| 115 | Expanded( |
| 116 | child: Text( |
| 117 | title, |
| 118 | style: titleTextStyle ?? |
| 119 | Theme.of(context).textTheme.bodyLarge?.copyWith( |
| 120 | color: textColor, |
| 121 | fontWeight: FontWeight.w700, |
| 122 | fontSize: 18, |
| 123 | ), |
| 124 | ), |
| 125 | ), |
| 126 | Row( |
| 127 | children: [ |
| 128 | if (leadingIcon != null) |
| 129 | Icon(leadingIcon, size: 16, color: textColor), |
| 130 | ], |
| 131 | ) |
| 132 | ], |
| 133 | ), |
| 134 | ), |
| 135 | ), |
| 136 | ], |
| 137 | ), |
| 138 | if (topLeftSubTitle != null || topRightSubTitle != null) |
| 139 | subTitleWidget( |
| 140 | leftSubTitle: topLeftSubTitle, |
| 141 | subTitleIconPath: leftSubTitleIconPath, |
| 142 | textColor: textColor, |
| 143 | rightSubTitle: topRightSubTitle, |
| 144 | rightSubTitleIconPath: rightSubTitleIconPath), |
| 145 | if (bottomLeftSubTitle != null || bottomRightSubTitle != null) |
| 146 | subTitleWidget( |
| 147 | leftSubTitle: bottomLeftSubTitle, |
| 148 | textColor: textColor, |
| 149 | subTitleFontSize: 12, |
| 150 | ), |
| 151 | if (badges != null && badges!.isNotEmpty) |
| 152 | Padding( |
| 153 | padding: const EdgeInsets.only(top: 12), |
| 154 | child: Row( |
| 155 | children: [ |
| 156 | ...badges! |
| 157 | .map( |
| 158 | (badge) => Badge( |
| 159 | title: badge, |
| 160 | textColor: badgeTextColor, |
| 161 | backgroundColor: badgeColor, |
| 162 | ), |
| 163 | ) |
| 164 | .toList() |
| 165 | ], |
| 166 | ), |
| 167 | ) |
| 168 | ], |
| 169 | ), |
| 170 | ), |
| 171 | ), |
| 172 | ); |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | class subTitleWidget extends StatelessWidget { |
| 177 | const subTitleWidget({ |
| 178 | super.key, |
| 179 | this.leftSubTitle, |
| 180 | this.subTitleIconPath, |
| 181 | required this.textColor, |
| 182 | this.rightSubTitle, |
| 183 | this.rightSubTitleIconPath, |
| 184 | this.subTitleFontSize = 16, |
| 185 | }); |
| 186 | |
| 187 | final String? leftSubTitle; |
| 188 | final String? subTitleIconPath; |
| 189 | final Color textColor; |
| 190 | final String? rightSubTitle; |
| 191 | final String? rightSubTitleIconPath; |
| 192 | final double subTitleFontSize; |
| 193 | |
| 194 | @override |
| 195 | Widget build(BuildContext context) { |
| 196 | return Row( |
| 197 | mainAxisAlignment: MainAxisAlignment.spaceBetween, |
| 198 | children: [ |
| 199 | leftSubTitle != null || subTitleIconPath != null |
| 200 | ? Row( |
| 201 | children: [ |
| 202 | if (subTitleIconPath != null && subTitleIconPath!.isNotEmpty) |
| 203 | Padding( |
| 204 | padding: const EdgeInsets.only(right: 6), |
| 205 | child: ImageUtil.getImageFromPath(imagePath: subTitleIconPath!), |
| 206 | ), |
| 207 | Text( |
| 208 | leftSubTitle ?? '', |
| 209 | style: Theme.of(context).textTheme.bodyMedium!.copyWith( |
| 210 | fontSize: subTitleFontSize, fontWeight: FontWeight.w700, color: textColor), |
| 211 | ), |
| 212 | ], |
| 213 | ) |
| 214 | : Offstage(), |
| 215 | rightSubTitle != null || rightSubTitleIconPath != null |
| 216 | ? Row( |
| 217 | children: [ |
| 218 | if (rightSubTitleIconPath != null && rightSubTitleIconPath!.isNotEmpty) |
| 219 | Padding( |
| 220 | padding: const EdgeInsets.only(right: 4), |
| 221 | child: ImageUtil.getImageFromPath(imagePath: rightSubTitleIconPath!), |
| 222 | ), |
| 223 | Text( |
| 224 | rightSubTitle ?? '', |
| 225 | style: Theme.of(context).textTheme.bodyMedium!.copyWith( |
| 226 | fontSize: subTitleFontSize, fontWeight: FontWeight.w700, color: textColor), |
| 227 | ), |
| 228 | ], |
| 229 | ) |
| 230 | : Offstage(), |
| 231 | ], |
| 232 | ); |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | class Badge extends StatelessWidget { |
| 237 | Badge({required this.textColor, required this.backgroundColor, required this.title}); |
| 238 | |
| 239 | final String title; |
| 240 | final Color textColor; |
| 241 | final Color backgroundColor; |
| 242 | |
| 243 | @override |
| 244 | Widget build(BuildContext context) { |
| 245 | return Padding( |
| 246 | padding: const EdgeInsets.only(right: 8), |
| 247 | child: FittedBox( |
| 248 | fit: BoxFit.fitHeight, |
| 249 | child: Container( |
| 250 | padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4), |
| 251 | decoration: BoxDecoration( |
| 252 | borderRadius: BorderRadius.all(Radius.circular(24)), color: backgroundColor), |
| 253 | alignment: Alignment.center, |
| 254 | child: Text( |
| 255 | title, |
| 256 | style: Theme.of(context).textTheme.bodySmall!.copyWith( |
| 257 | color: textColor, |
| 258 | fontWeight: FontWeight.w600, |
| 259 | ), |
| 260 | ), |
| 261 | ), |
| 262 | ), |
| 263 | ); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | class OptionTilePlaceholder extends StatefulWidget { |
| 268 | OptionTilePlaceholder({ |
| 269 | this.borderRadius, |
| 270 | this.imageHeight, |
| 271 | this.imageWidth, |
| 272 | this.padding, |
| 273 | this.leadingIcon, |
| 274 | this.withBadge = true, |
| 275 | this.withSubtitle = true, |
| 276 | this.isDarkTheme = false, |
| 277 | this.errorText, |
| 278 | }); |
| 279 | |
| 280 | final double? borderRadius; |
| 281 | final double? imageHeight; |
| 282 | final double? imageWidth; |
| 283 | final EdgeInsets? padding; |
| 284 | final IconData? leadingIcon; |
| 285 | final bool withBadge; |
| 286 | final bool withSubtitle; |
| 287 | final bool isDarkTheme; |
| 288 | final String? errorText; |
| 289 | |
| 290 | @override |
| 291 | _OptionTilePlaceholderState createState() => _OptionTilePlaceholderState(); |
| 292 | } |
| 293 | |
| 294 | class _OptionTilePlaceholderState extends State<OptionTilePlaceholder> |
| 295 | with SingleTickerProviderStateMixin { |
| 296 | late AnimationController _controller; |
| 297 | late Animation<double> _animation; |
| 298 | |
| 299 | @override |
| 300 | void initState() { |
| 301 | super.initState(); |
| 302 | |
| 303 | _controller = AnimationController( |
| 304 | duration: const Duration(seconds: 2), |
| 305 | vsync: this, |
| 306 | )..repeat(); |
| 307 | |
| 308 | _animation = CurvedAnimation( |
| 309 | parent: _controller, |
| 310 | curve: Curves.linear, |
| 311 | ); |
| 312 | } |
| 313 | |
| 314 | @override |
| 315 | void dispose() { |
| 316 | _controller.dispose(); |
| 317 | super.dispose(); |
| 318 | } |
| 319 | |
| 320 | @override |
| 321 | Widget build(BuildContext context) { |
| 322 | final backgroundColor = Theme.of(context).colorScheme.surfaceContainer; |
| 323 | final titleColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.4); |
| 324 | |
| 325 | return widget.errorText != null |
| 326 | ? Container( |
| 327 | width: double.infinity, |
| 328 | padding: widget.padding ?? EdgeInsets.all(16), |
| 329 | alignment: Alignment.center, |
| 330 | decoration: BoxDecoration( |
| 331 | borderRadius: BorderRadius.all( |
| 332 | Radius.circular(widget.borderRadius ?? 12), |
| 333 | ), |
| 334 | color: backgroundColor, |
| 335 | ), |
| 336 | child: Column( |
| 337 | children: [ |
| 338 | Text( |
| 339 | widget.errorText!, |
| 340 | style: Theme.of(context).textTheme.bodyMedium!.copyWith( |
| 341 | color: titleColor, |
| 342 | fontSize: 16, |
| 343 | ), |
| 344 | ), |
| 345 | if (widget.withSubtitle) SizedBox(height: 8), |
| 346 | Text( |
| 347 | '', |
| 348 | style: Theme.of(context).textTheme.bodyMedium!.copyWith( |
| 349 | color: titleColor, |
| 350 | fontSize: 16, |
| 351 | ), |
| 352 | ), |
| 353 | ], |
| 354 | ), |
| 355 | ) |
| 356 | : AnimatedBuilder( |
| 357 | animation: _animation, |
| 358 | builder: (context, child) { |
| 359 | return Stack( |
| 360 | children: [ |
| 361 | Container( |
| 362 | width: double.infinity, |
| 363 | padding: widget.padding ?? EdgeInsets.all(16), |
| 364 | alignment: Alignment.center, |
| 365 | decoration: BoxDecoration( |
| 366 | borderRadius: BorderRadius.all(Radius.circular(widget.borderRadius ?? 12)), |
| 367 | color: backgroundColor, |
| 368 | ), |
| 369 | child: Column( |
| 370 | crossAxisAlignment: CrossAxisAlignment.center, |
| 371 | children: [ |
| 372 | Row( |
| 373 | children: [ |
| 374 | Container( |
| 375 | height: widget.imageHeight ?? 35, |
| 376 | width: widget.imageWidth ?? 35, |
| 377 | decoration: BoxDecoration( |
| 378 | color: titleColor, |
| 379 | shape: BoxShape.circle, |
| 380 | ), |
| 381 | ), |
| 382 | SizedBox(width: 8), |
| 383 | Expanded( |
| 384 | child: Container( |
| 385 | child: Row( |
| 386 | mainAxisAlignment: MainAxisAlignment.spaceBetween, |
| 387 | children: [ |
| 388 | Container( |
| 389 | height: 20, |
| 390 | width: 70, |
| 391 | decoration: BoxDecoration( |
| 392 | color: titleColor, |
| 393 | borderRadius: BorderRadius.all(Radius.circular(8)), |
| 394 | ), |
| 395 | ), |
| 396 | if (widget.leadingIcon != null) |
| 397 | Icon(widget.leadingIcon, size: 16, color: titleColor), |
| 398 | ], |
| 399 | ), |
| 400 | ), |
| 401 | ), |
| 402 | ], |
| 403 | ), |
| 404 | if (widget.withSubtitle) |
| 405 | Padding( |
| 406 | padding: EdgeInsets.symmetric(vertical: 8), |
| 407 | child: Row( |
| 408 | mainAxisAlignment: MainAxisAlignment.spaceBetween, |
| 409 | children: [ |
| 410 | Container( |
| 411 | height: 20, |
| 412 | width: 170, |
| 413 | decoration: BoxDecoration( |
| 414 | color: titleColor, |
| 415 | borderRadius: BorderRadius.all(Radius.circular(8)), |
| 416 | ), |
| 417 | ), |
| 418 | ], |
| 419 | ), |
| 420 | ), |
| 421 | if (widget.withBadge) |
| 422 | Padding( |
| 423 | padding: const EdgeInsets.only(top: 16.0), |
| 424 | child: Row( |
| 425 | children: [ |
| 426 | Container( |
| 427 | height: 30, |
| 428 | width: 70, |
| 429 | decoration: BoxDecoration( |
| 430 | color: titleColor, |
| 431 | borderRadius: BorderRadius.all(Radius.circular(20)), |
| 432 | ), |
| 433 | ), |
| 434 | SizedBox(width: 8), |
| 435 | Container( |
| 436 | height: 30, |
| 437 | width: 70, |
| 438 | decoration: BoxDecoration( |
| 439 | color: titleColor, |
| 440 | borderRadius: BorderRadius.all(Radius.circular(20)), |
| 441 | ), |
| 442 | ), |
| 443 | ], |
| 444 | ), |
| 445 | ), |
| 446 | ], |
| 447 | ), |
| 448 | ), |
| 449 | Positioned.fill( |
| 450 | child: Container( |
| 451 | decoration: BoxDecoration( |
| 452 | borderRadius: BorderRadius.all(Radius.circular(widget.borderRadius ?? 12)), |
| 453 | gradient: LinearGradient( |
| 454 | begin: Alignment(-2, -4), |
| 455 | end: Alignment(2, 4), |
| 456 | stops: [ |
| 457 | _animation.value - 0.2, |
| 458 | _animation.value, |
| 459 | _animation.value + 0.2, |
| 460 | ], |
| 461 | colors: [ |
| 462 | backgroundColor.withOpacity(widget.isDarkTheme ? 0.4 : 0.7), |
| 463 | backgroundColor.withOpacity(widget.isDarkTheme ? 0.7 : 0.4), |
| 464 | backgroundColor.withOpacity(widget.isDarkTheme ? 0.4 : 0.7), |
| 465 | ], |
| 466 | ), |
| 467 | ), |
| 468 | ), |
| 469 | ), |
| 470 | ], |
| 471 | ); |
| 472 | }, |
| 473 | ); |
| 474 | } |
| 475 | } |