Changing Elevated Button Background Color in Flutter
Learn how to properly customize the background color of ElevatedButton widgets in Flutter using modern approaches and best practices.
Problem Statement
When working with Flutter's ElevatedButton, developers often encounter errors when trying to customize the button's background color. The common error occurs:
type '_MaterialStatePropertyAll' is not a subtype of type 'MaterialStateProperty<Color?>?
This typically happens when:
- Using the wrong approach to set button colors
- Passing incorrect parameter types to styling properties
- Not accounting for Flutter version changes in the API
Solution Overview
Flutter provides several methods to customize ElevatedButton colors. The two primary approaches are:
- Using
ElevatedButton.styleFrom()
- Simplified approach for basic customization - Using
ButtonStyle
withMaterialStateProperty
- More control over different button states
Recommended Solutions
Method 1: Using ElevatedButton.styleFrom() (Recommended)
The simplest and most straightforward approach for most use cases:
ElevatedButton(
onPressed: () {},
child: Text('Click Me'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Background color
foregroundColor: Colors.white, // Text/icon color
padding: EdgeInsets.all(16.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
)
Method 2: Using ButtonStyle with MaterialStateProperty
For more advanced control over different button states (pressed, disabled, hovered, etc.):
ElevatedButton(
onPressed: () {},
child: Text('Click Me'),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(MaterialState.pressed)) {
return Colors.blue.shade700; // Color when pressed
} else if (states.contains(MaterialState.disabled)) {
return Colors.grey; // Color when disabled
}
return Colors.blue; // Default color
},
),
foregroundColor: MaterialStateProperty.all(Colors.white),
),
)
Method 3: Dynamic Color Changes Based on State
For buttons that need to change color based on application state:
bool _isActive = false;
ElevatedButton(
onPressed: () {
setState(() {
_isActive = !_isActive;
});
},
child: Text(_isActive ? 'Active' : 'Inactive'),
style: ElevatedButton.styleFrom(
backgroundColor: _isActive ? Colors.green : Colors.red,
foregroundColor: Colors.white,
),
)
Common Pitfalls and Solutions
WARNING
Important Version Note: As of Flutter v3.1.0, the primary
parameter in ElevatedButton.styleFrom()
is deprecated. Use backgroundColor
instead.
Incorrect approach (deprecated):
// ❌ Don't use - deprecated after Flutter v3.1.0
ElevatedButton.styleFrom(primary: Colors.red)
Correct approach:
// ✅ Use this instead
ElevatedButton.styleFrom(backgroundColor: Colors.red)
TIP
Always ensure your onPressed
callback is properly defined. Buttons without an onPressed
handler will appear disabled with gray colors regardless of your custom styling.
Complete Example: Xylophone App Buttons
Here's the corrected implementation for the xylophone application mentioned in the question:
void playSound(int soundNumber) {
final player = AudioCache();
player.play('note$soundNumber.wav');
}
Expanded buildPlayButton({required Color color, required int soundNumber}) {
return Expanded(
child: ElevatedButton(
onPressed: () {
playSound(soundNumber);
},
style: ElevatedButton.styleFrom(
backgroundColor: color,
foregroundColor: Colors.white,
),
),
);
}
// Usage in build method
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
buildPlayButton(color: Colors.red, soundNumber: 1),
buildPlayButton(color: Colors.orange, soundNumber: 2),
buildPlayButton(color: Colors.yellow, soundNumber: 3),
buildPlayButton(color: Colors.green, soundNumber: 4),
buildPlayButton(color: Colors.blue, soundNumber: 5),
buildPlayButton(color: Colors.indigo, soundNumber: 6),
buildPlayButton(color: Colors.purple, soundNumber: 7),
],
);
}
Additional Styling Options
Beyond background color, you can customize various aspects of ElevatedButton:
ElevatedButton(
onPressed: () {},
child: Text('Styled Button'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
elevation: 5,
shadowColor: Colors.deepPurple.shade300,
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
textStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
)
Best Practices
- Use
styleFrom()
for simple customizations - It's more concise and readable - Use
ButtonStyle
for state-dependent styling - When you need different colors for pressed, hovered, or disabled states - Ensure contrast between background and text colors - For accessibility
- Test your buttons in different states - Especially disabled and pressed states
- Consider using theme colors - For consistency across your app
// Using theme colors
ElevatedButton(
onPressed: () {},
child: Text('Themed Button'),
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
),
)
By following these approaches, you can effectively customize ElevatedButton colors in your Flutter applications while avoiding common pitfalls and ensuring your code remains maintainable across Flutter version updates.