dev
dart 104 lines 3.23 KB
Raw
1 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
2 import 'package:flutter/material.dart';
3 import 'package:flutter_svg/flutter_svg.dart';
4
5 class ModernButton extends StatelessWidget {
6 final double size;
7 final String? svgPath;
8 final Widget? icon;
9 final VoidCallback onPressed;
10 final Color? iconColor;
11 final Color? backgroundColor;
12 final double? iconSize;
13 final String? label;
14
15 /// Accessible name announced by screen readers. Falls back to [label] (the
16 /// visible caption) when omitted, so one of the two is required. Must be
17 /// localized by the caller.
18 final String? semanticLabel;
19 static const iconSvgSizeRatio = 2 / 3;
20
21 const ModernButton(
22 {super.key,
23 required this.size,
24 required this.icon,
25 required this.onPressed,
26 this.iconSize,
27 this.iconColor,
28 this.backgroundColor,
29 this.label,
30 this.semanticLabel})
31 : svgPath = null;
32
33 const ModernButton.svg(
34 {super.key,
35 required this.size,
36 required this.svgPath,
37 required this.onPressed,
38 this.iconSize,
39 this.iconColor,
40 this.backgroundColor,
41 this.label,
42 this.semanticLabel})
43 : icon = null;
44
45 @override
46 Widget build(BuildContext context) {
47 final resolvedIconColor = iconColor ?? Theme.of(context).colorScheme.primary;
48 final resolvedIconSize = iconSize ?? size * (svgPath != null ? 1 : iconSvgSizeRatio);
49 final resolvedBackgroundColor =
50 backgroundColor ?? Theme.of(context).colorScheme.surfaceContainer;
51 final Widget resolvedIcon = svgPath != null
52 ? CakeImageWidget(
53 imageUrl: svgPath!,
54 width: resolvedIconSize,
55 height: resolvedIconSize,
56 fit: BoxFit.contain,
57 alignment: Alignment.center,
58 allowDrawingOutsideViewBox: true,
59 colorFilter: ColorFilter.mode(resolvedIconColor, BlendMode.srcIn),
60 )
61 : IconTheme(
62 data: IconThemeData(color: resolvedIconColor, size: resolvedIconSize),
63 child: icon!,
64 );
65
66 // A single accessibility node for the whole control: the visible caption
67 // duplicates the accessible name, so it is excluded to avoid a second,
68 // detached stop for screen readers.
69 return MergeSemantics(
70 child: Semantics(
71 button: true,
72 label: semanticLabel ?? label,
73 child: Column(
74 spacing: 8,
75 children: [
76 Container(
77 decoration: BoxDecoration(
78 color: resolvedBackgroundColor,
79 borderRadius: BorderRadius.circular(size),
80 ),
81 width: size,
82 height: size,
83 child: IconButton(
84 padding: EdgeInsets.zero,
85 onPressed: onPressed,
86 icon: ExcludeSemantics(child: resolvedIcon),
87 ),
88 ),
89 if (label != null && label!.isNotEmpty)
90 ExcludeSemantics(
91 child: Text(
92 label!,
93 style: TextStyle(
94 fontSize: 12,
95 fontWeight: FontWeight.w400,
96 color: Theme.of(context).colorScheme.onSurface),
97 ),
98 )
99 ],
100 ),
101 ),
102 );
103 }
104 }