At this point, Bullseye is starting to look great, but we still need to add some styling to the buttons. In the previous episode, we styled Text widgets by creating style classes that we applied to the widgets.
An alternative way to add styling is to create your own widgets that return styled widgets in their build methods. Then you can use those widgets in place of similar widgets in our app. In this example, a StyledButton class is a RawMaterialButton with an icon, a circular border, and some particular colors.
In this episode we’ll use a StyledButton to replace some of our TextButtons, and we’ll do similar for the Hit Me button.
To get started, open up your project in progress or download the starter project for this episode. We’re going to create a new button. Create a new file and call it styled_button.dart.
The first thing we’ve done is import the material library. Remember, this widget gives us all our widgets.
import 'package:flutter/material.dart';
Next, we’ll create a new stateless widget. Type ST then select the StatelessWidget option. Give it the name, StyledButton.
class StyledButton extends StatelessWidget {
const StyledButton({ Key? key }) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
);
}
}
This widget will contain two different properties. It will contain one property for an icon. This property will be a type of IconData. This is data that flutter provides so we can display the icon. We also pass in a callback.
final IconData icon;
final GestureTapCallback onPressed;
Next, update the constructor to set those properties.
const StyledButton({Key? key, required this.icon, required this.onPressed})
: super(key: key);
Now, let’s update our build method. First we’ll return a RawMaterialButton, setting the fill color and splash color.
return RawMaterialButton(
fillColor: Colors.black,
splashColor: Colors.redAccent)
We’ll add an Icon widget to it. First we will wrap it in padding, then set the icon with a white color.
return RawMaterialButton(
fillColor: Colors.black, // old
splashColor: Colors.redAccent, // old
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
icon,
color: Colors.white,
),
),
);
Next, we’ll assign the onPressed callback and add a shape.
return RawMaterialButton(
fillColor: Colors.black,
splashColor: Colors.redAccent,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
icon,
color: Colors.white,
),
), // old code
onPressed: onPressed,
shape: const CircleBorder(side: BorderSide(color: Colors.white)),
);
Okay, our button is ready to be used. Open up score.dart and import our new styled button class.
import 'styled_button.dart';
Now, let’s replace the text buttons with our new styled button. First, we’ll update the Start Over button.
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ // old code
StyledButton(
icon: Icons.refresh,
onPressed: () {
onStartOver();
},
),
Now lets replace our info button.
Padding(
padding: const EdgeInsets.only(left: 32, right: 32.0),
child: Column(
children: <Widget>[
Text('Round:', style: LabelTextStyle.bodyText1(context)),
Text('$round', style: ScoreNumberTextStyle.headline4(context)),
],
),
), // old code
StyledButton(
icon: Icons.info,
onPressed: () {},
),
Now build and run or hot reload the the app. Now you’ll see that we have a couple of new styling buttons.
Okay, now let’s create a new button for the Hit Me button. Create a new file called hit_me_button.dart. Import the material library.
import 'package:flutter/material.dart';
Now type ST and select the Stateless Widget option. Give it the name HitMeButton.
class HitMeButton extends StatelessWidget {
const HitMeButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container();
}
}
This will have two properties: a string for the button’s text and a callback.
final String text;
final GestureTapCallback onPressed;
Then update the the constructor.
const HitMeButton({Key? key, required this.text, required this.onPressed})
: super(key: key);
Next we’ll create a new RawMaterial button and add some styling to it.
return RawMaterialButton(
fillColor: Colors.red[700],
splashColor: Colors.redAccent,
child: Padding(
padding: const EdgeInsets.all(14.0),
child: Text(
text,
maxLines: 1,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
),
),
),
Notice that we created a new text style this time, setting bolded white lettering with increased spacing. Let’s add an onPressed event with a rounded shape to it.
@override
Widget build(BuildContext context) {
return RawMaterialButton(
fillColor: Colors.red[700],
splashColor: Colors.redAccent,
child: Padding(
padding: const EdgeInsets.all(14.0),
child: Text(
text,
maxLines: 1,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
),
),
), // old code
onPressed: onPressed,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
side: const BorderSide(
color: Colors.white,
),
),
);
}
Now let’s open up main.dart and and import hit_me_button.dart
import 'hit_me_button.dart';
Now scroll down to the Hit Me Text Button and replace it with our new button.
HitMeButton(
text: 'HIT ME',
onPressed: () {
_showAlert(context);
},
),
Now if you switch back to the app, you’ll see our app is looking better. One last fix, let’s update the button in the alert. First, we need to import the styled button. Add the following:
import 'styled_button.dart';
Now scroll down to _showAlert and update it replace the TextButton with the StyledButton.
var okButton = StyledButton(
icon: Icons.close,
onPressed: () {
Navigator.of(context).pop();
setState(() {
_model.totalScore += _pointsForCurrentRound();
_model.target = Random().nextInt(100) + 1;
_model.round += 1;
});
},
);
Now return back to our app. You’ll see that we have our buttons. When you tap the Hit Me button, we have a new close button. Things are a light cramped, but before we address the padding, we need to update our slider and we’ll do that in the next episode.