Skip to content

Commit ecbff15

Browse files
authored
refactor(ui): refactor platform specific widget styling (#8333)
1 parent 1937f27 commit ecbff15

8 files changed

+226
-118
lines changed

packages/flutterfire_ui/lib/src/auth/widgets/delete_account_button.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import 'package:flutter/material.dart';
12
import 'package:firebase_auth/firebase_auth.dart'
23
show FirebaseAuth, FirebaseAuthException;
34
import 'package:flutter/cupertino.dart';
45
import 'package:flutterfire_ui/auth.dart';
56
import 'package:flutterfire_ui/i10n.dart';
67
import '../widgets/internal/loading_button.dart';
7-
import 'package:flutter/material.dart';
8+
import '../widgets/internal/universal_button.dart';
89

910
typedef DeleteFailedCallback = void Function(Exception exception);
1011
typedef SignInRequiredCallback = Future<bool> Function();
@@ -13,12 +14,14 @@ class DeleteAccountButton extends StatefulWidget {
1314
final FirebaseAuth? auth;
1415
final SignInRequiredCallback? onSignInRequired;
1516
final DeleteFailedCallback? onDeleteFailed;
17+
final ButtonVariant? variant;
1618

1719
const DeleteAccountButton({
1820
Key? key,
1921
this.auth,
2022
this.onSignInRequired,
2123
this.onDeleteFailed,
24+
this.variant,
2225
}) : super(key: key);
2326

2427
@override
@@ -67,6 +70,7 @@ class _DeleteAccountButtonState extends State<DeleteAccountButton> {
6770
icon: isCupertino ? CupertinoIcons.delete : Icons.delete,
6871
label: l.deleteAccount,
6972
onTap: _deleteAccount,
73+
variant: widget.variant,
7074
);
7175
}
7276
}
Lines changed: 33 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
import 'package:flutter/cupertino.dart';
2-
import 'package:flutter/material.dart';
32
import 'package:flutterfire_ui/auth.dart';
43

5-
class LoadingButton extends StatelessWidget {
6-
final bool isLoading;
7-
final String label;
8-
final IconData? icon;
9-
final Color? color;
10-
final VoidCallback onTap;
4+
import 'universal_button.dart';
115

12-
const LoadingButton({
6+
class _LoadingButtonContent extends StatelessWidget {
7+
final String label;
8+
final bool isLoading;
9+
const _LoadingButtonContent({
1310
Key? key,
1411
required this.label,
15-
required this.onTap,
16-
this.isLoading = false,
17-
this.icon,
18-
this.color,
12+
required this.isLoading,
1913
}) : super(key: key);
2014

2115
@override
@@ -31,59 +25,38 @@ class LoadingButton extends StatelessWidget {
3125
);
3226
}
3327

34-
ButtonStyle? style;
35-
36-
if (color != null) {
37-
style = ButtonStyle(
38-
foregroundColor: MaterialStateColor.resolveWith(
39-
(states) => color!,
40-
),
41-
overlayColor: MaterialStateColor.resolveWith(
42-
(states) => color!.withAlpha(20),
43-
),
44-
);
45-
}
46-
47-
if (isCupertino) {
48-
if (icon != null) {
49-
child = Row(
50-
mainAxisSize: MainAxisSize.min,
51-
children: <Widget>[
52-
Icon(icon, size: 20),
53-
const SizedBox(width: 8),
54-
child,
55-
],
56-
);
57-
}
28+
return child;
29+
}
30+
}
5831

59-
final button = CupertinoButton.filled(
60-
onPressed: onTap,
61-
child: child,
62-
);
32+
class LoadingButton extends StatelessWidget {
33+
final bool isLoading;
34+
final String label;
35+
final IconData? icon;
36+
final Color? color;
37+
final VoidCallback onTap;
38+
final ButtonVariant? variant;
6339

64-
return color != null
65-
? CupertinoTheme(
66-
data: CupertinoTheme.of(context).copyWith(
67-
primaryColor: color,
68-
),
69-
child: button,
70-
)
71-
: button;
72-
}
40+
const LoadingButton({
41+
Key? key,
42+
required this.label,
43+
required this.onTap,
44+
this.isLoading = false,
45+
this.icon,
46+
this.color,
47+
this.variant = ButtonVariant.text,
48+
}) : super(key: key);
7349

74-
if (icon != null) {
75-
return OutlinedButton.icon(
76-
onPressed: onTap,
77-
icon: Icon(icon),
78-
label: child,
79-
style: style,
80-
);
81-
}
50+
@override
51+
Widget build(BuildContext context) {
52+
final content = _LoadingButtonContent(label: label, isLoading: isLoading);
8253

83-
return OutlinedButton(
54+
return UniversalButton(
55+
color: color,
56+
icon: icon,
8457
onPressed: onTap,
85-
style: style,
86-
child: child,
58+
variant: variant,
59+
child: content,
8760
);
8861
}
8962
}

packages/flutterfire_ui/lib/src/auth/widgets/internal/platform_widget.dart

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,27 @@ abstract class PlatformWidget extends StatelessWidget {
66
Widget buildCupertino(BuildContext context);
77
Widget buildMaterial(BuildContext context);
88

9+
Widget? buildWrapper(BuildContext context, Widget child) {
10+
return null;
11+
}
12+
913
@override
1014
Widget build(BuildContext context) {
1115
final isCupertino = CupertinoUserInterfaceLevel.maybeOf(context) != null;
16+
late Widget child;
1217

1318
if (isCupertino) {
14-
return buildCupertino(context);
19+
child = buildCupertino(context);
20+
} else {
21+
child = buildMaterial(context);
22+
}
23+
24+
final wrapper = buildWrapper(context, child);
25+
26+
if (wrapper == null) {
27+
return child;
1528
} else {
16-
return buildMaterial(context);
29+
return wrapper;
1730
}
1831
}
1932
}
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import 'package:flutter/cupertino.dart';
22
import 'package:flutter/material.dart';
3+
import 'platform_widget.dart';
34

4-
class Subtitle extends StatelessWidget {
5+
class Subtitle extends PlatformWidget {
56
final String text;
67
const Subtitle({Key? key, required this.text}) : super(key: key);
78

89
@override
9-
Widget build(BuildContext context) {
10-
final isCupertino = CupertinoUserInterfaceLevel.maybeOf(context) != null;
11-
final titleStyle = isCupertino
12-
? CupertinoTheme.of(context).textTheme.navTitleTextStyle
13-
: Theme.of(context).textTheme.subtitle1;
10+
Widget buildCupertino(BuildContext context) {
11+
return Text(
12+
text,
13+
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
14+
);
15+
}
1416

17+
@override
18+
Widget buildMaterial(BuildContext context) {
1519
return Text(
1620
text,
17-
style: titleStyle,
21+
style: Theme.of(context).textTheme.subtitle1,
1822
);
1923
}
2024
}

packages/flutterfire_ui/lib/src/auth/widgets/internal/universal_button.dart

Lines changed: 99 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,78 +6,144 @@ import 'platform_widget.dart';
66
enum ButtonVariant {
77
text,
88
filled,
9+
outlined,
910
}
1011

1112
class UniversalButton extends PlatformWidget {
1213
final VoidCallback? onPressed;
13-
final String text;
14+
final String? text;
15+
final Widget? child;
1416
final IconData? icon;
1517
final TextDirection? direction;
16-
final ButtonVariant variant;
18+
final ButtonVariant? variant;
19+
final Color? color;
1720

1821
const UniversalButton({
1922
Key? key,
20-
required this.text,
23+
this.text,
24+
this.child,
2125
this.onPressed,
2226
this.icon,
23-
this.direction,
24-
this.variant = ButtonVariant.filled,
25-
}) : super(key: key);
27+
this.direction = TextDirection.ltr,
28+
this.variant,
29+
this.color,
30+
}) : assert(text != null || child != null),
31+
super(key: key);
32+
33+
ButtonVariant get _variant {
34+
return variant ?? ButtonVariant.filled;
35+
}
2636

2737
@override
2838
Widget buildCupertino(BuildContext context) {
39+
late Widget button;
40+
2941
final child = Row(
3042
textDirection: direction,
3143
mainAxisAlignment: MainAxisAlignment.center,
3244
children: [
33-
Text(text),
34-
if (icon != null) ...[const SizedBox(width: 8), Icon(icon, size: 20)],
45+
if (icon != null) ...[
46+
if (direction == TextDirection.rtl) const SizedBox(width: 8),
47+
Icon(icon, size: 20),
48+
if (direction == TextDirection.ltr) const SizedBox(width: 8),
49+
],
50+
this.child ?? Text(text!),
3551
],
3652
);
3753

38-
if (variant == ButtonVariant.text) {
39-
return CupertinoButton(
54+
if (_variant == ButtonVariant.text || _variant == ButtonVariant.outlined) {
55+
button = CupertinoButton(
4056
padding: EdgeInsets.zero,
4157
onPressed: onPressed,
4258
child: child,
4359
);
4460
} else {
45-
return CupertinoButton.filled(
61+
button = CupertinoButton.filled(
4662
onPressed: onPressed,
4763
child: child,
4864
);
4965
}
66+
67+
if (color != null) {
68+
return CupertinoTheme(
69+
data: CupertinoTheme.of(context).copyWith(primaryColor: color),
70+
child: button,
71+
);
72+
} else {
73+
return button;
74+
}
5075
}
5176

5277
@override
5378
Widget buildMaterial(BuildContext context) {
54-
final child = Text(text);
79+
final child = this.child ?? Text(text!);
80+
81+
ButtonStyle? style;
82+
83+
if (color != null) {
84+
MaterialStateColor? foregroundColor;
85+
MaterialStateColor? backgroundColor;
5586

56-
if (icon != null) {
5787
if (variant == ButtonVariant.text) {
58-
return TextButton.icon(
59-
icon: Icon(icon),
60-
onPressed: onPressed,
61-
label: child,
62-
);
88+
foregroundColor = MaterialStateColor.resolveWith((_) => color!);
6389
} else {
64-
return ElevatedButton.icon(
65-
icon: Icon(icon),
66-
onPressed: onPressed,
67-
label: child,
68-
);
90+
backgroundColor = MaterialStateColor.resolveWith((_) => color!);
6991
}
70-
} else {
71-
if (variant == ButtonVariant.text) {
72-
return TextButton(
73-
onPressed: onPressed,
74-
child: child,
75-
);
76-
} else {}
77-
return ElevatedButton(
78-
onPressed: onPressed,
79-
child: child,
92+
93+
style = ButtonStyle(
94+
foregroundColor: foregroundColor,
95+
backgroundColor: backgroundColor,
96+
overlayColor: MaterialStateColor.resolveWith(
97+
(states) => color!.withAlpha(20),
98+
),
8099
);
81100
}
101+
102+
if (icon != null) {
103+
switch (_variant) {
104+
case ButtonVariant.text:
105+
return TextButton.icon(
106+
icon: Icon(icon),
107+
onPressed: onPressed,
108+
label: child,
109+
style: style,
110+
);
111+
case ButtonVariant.filled:
112+
return ElevatedButton.icon(
113+
onPressed: onPressed,
114+
icon: Icon(icon),
115+
label: child,
116+
style: style,
117+
);
118+
case ButtonVariant.outlined:
119+
return OutlinedButton.icon(
120+
onPressed: onPressed,
121+
icon: Icon(icon),
122+
label: child,
123+
style: style,
124+
);
125+
}
126+
} else {
127+
switch (_variant) {
128+
case ButtonVariant.text:
129+
return TextButton(
130+
onPressed: onPressed,
131+
style: style,
132+
child: child,
133+
);
134+
case ButtonVariant.filled:
135+
return ElevatedButton(
136+
onPressed: onPressed,
137+
style: style,
138+
child: child,
139+
);
140+
case ButtonVariant.outlined:
141+
return OutlinedButton(
142+
onPressed: onPressed,
143+
style: style,
144+
child: child,
145+
);
146+
}
147+
}
82148
}
83149
}

0 commit comments

Comments
 (0)