I'm using OutlinedButton
s as options in a form that can be selected. When an OutlinedButton
is pressed, I want to make the stroke thicker, the text bolder, and add a small circular indicator.
I'm able to make the stroke thicker and add the indicator, but I'm having a really hard time making the text bolder. Here is my most recent code:
/***
The OutlinedButton
***/
class OutlinedButtonChoice extends StatefulWidget {
final bool isSelected;
final void Function() onPressed;
final String label;
const OutlinedButtonChoice({
super.key,
required this.isSelected,
required this.onPressed,
required this.label,
});
@override
State<OutlinedButtonChoice> createState() => _OutlinedButtonChoiceState();
}
class _OutlinedButtonChoiceState extends State<OutlinedButtonChoice> {
late WidgetStatesController _statesController;
@override
void initState() {
super.initState();
_statesController = WidgetStatesController(<WidgetState>{
if (widget.isSelected) WidgetState.selected,
});
}
@override
void dispose() {
_statesController.dispose();
super.dispose();
}
@override
void didUpdateWidget(OutlinedButtonChoice oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.isSelected != oldWidget.isSelected) {
_statesController.update(WidgetState.selected, widget.isSelected);
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final baseStyle =
theme.outlinedButtonTheme.style ?? OutlinedButton.styleFrom();
final realStyle = baseStyle.copyWith(
minimumSize: const WidgetStatePropertyAll(Size(0, 50)),
);
return OutlinedButton(
onPressed: widget.onPressed,
style: realStyle,
statesController: _statesController,
child: Row(
spacing: 5.0,
children: [
Text(widget.label),
if (widget.isSelected)
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: theme.primaryColor,
),
),
],
),
);
}
}
/***
Truncated custom ThemeData
***/
final bodyLarge = GoogleFonts.roboto(fontSize: 22, fontWeight: FontWeight.w300);
final myThemeData = ThemeData(
outlinedButtonTheme: OutlinedButtonThemeData(
style: ButtonStyle(
foregroundColor: WidgetStatePropertyAll(_brandColor),
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
),
side: WidgetStateProperty.resolveWith<BorderSide>((states) {
final width = states.contains(WidgetState.selected) ? 2.5 : 1.0;
return BorderSide(width: width, color: _brandColor);
}),
textStyle: WidgetStateProperty.resolveWith<TextStyle?>((states) {
if (states.contains(WidgetState.selected)) {
return textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w800);
}
return textTheme.bodyLarge;
}),
),
),
);
Please assume that I'm passing isSelected
correctly to the OutlinedButtonChoice
. Other information:
Flutter 3.32.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 6fba2447e9 (5 days ago) • 2025-06-12 19:03:56 -0700
Engine • revision 8cd19e509d (6 days ago) • 2025-06-12 16:30:12 -0700
Tools • Dart 3.8.1 • DevTools 2.45.1
Platform: MacOS
The only way I've been able to get this to work is to assign a style to the Text
child of the OutlinedButton
, but that feels rather brittle. If this isn't possible, is there perhaps another widget I can use? Thank you!
you are right but there is a catch , when a Text
widget is passed as a child to a button, any direct style:
applied on that Text
will override what the button's textStyle
attempts to provide.
like in your case
Text(widget.label)
Wrap your label + indicator in a Builder
, and manually apply the text style from the current button Theme
. Here’s how you can do it properly:
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final buttonStyle = theme.outlinedButtonTheme.style ?? OutlinedButton.styleFrom();
final effectiveTextStyle = buttonStyle.textStyle?.resolve(_statesController.value) ??
DefaultTextStyle.of(context).style;
return OutlinedButton(
onPressed: widget.onPressed,
style: buttonStyle.copyWith(
minimumSize: const WidgetStatePropertyAll(Size(0, 50)),
),
statesController: _statesController,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget.label,
style: effectiveTextStyle, // manually apply the resolved style
),
if (widget.isSelected) ...[
const SizedBox(width: 8),
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: theme.primaryColor,
),
),
],
],
),
);
}