前編では、電卓アプリの全体レイアウトを構築し、結果表示部分とボタン部分のUIを作成しました。後編では、ボタンの配置を調整し、カスタムウィジェットを使ってボタンを共通化します。
4. ボタンの配置とデザイン
前編で作成したボタン部分のレイアウトは、まだ改善の余地があります。ボタンの間隔が狭すぎることと、イコールボタンが他のボタンと同じサイズであることが気になります。
ボタンの間隔を調整するには、Row
の mainAxisAlignment
プロパティと crossAxisAlignment
プロパティを使用します。
mainAxisAlignment
: 主軸方向(Row
の場合は水平方向)の配置を制御するcrossAxisAlignment
: 交差軸方向(Row
の場合は垂直方向)の配置を制御する
dart
Container(
height: 350.0, // Expandedを削除したためContainerの高さを調整
color: Colors.black,
child: Column(
children: <Widget>[
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, // 追加
children: <Widget>[
_buildButton('7'),
_buildButton('8'),
_buildButton('9'),
_buildButton('/'),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, // 追加
children: <Widget>[
_buildButton('4'),
_buildButton('5'),
_buildButton('6'),
_buildButton('x'),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, // 追加
children: <Widget>[
_buildButton('1'),
_buildButton('2'),
_buildButton('3'),
_buildButton('-'),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, // 追加
children: <Widget>[
_buildButton('.'),
_buildButton('0'),
_buildButton('C'),
_buildButton('+'),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch, // 追加
children: <Widget>[
_buildButton('=', flex: 2, color: Colors.orange), // 変更
],
),
),
],
),
),
各々のRow
ウィジェットをExpanded
でラップしました。また、Row
の crossAxisAlignment
プロパティを CrossAxisAlignment.stretch
に設定しました。これにより、ボタンが垂直方向にも広がるようになります。
_buildButton
関数にflex
とcolor
の引数を追加し、ボタンの大きさと色を変えられるように変更しました。
dart
Widget _buildButton(
String buttonText, {
int flex = 1,
Color color = Colors.grey,
}) {
return Expanded(
flex: flex, // 追加
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: color,
padding: const EdgeInsets.all(24.0),
shape: const RoundedRectangleBorder(
side: BorderSide(
color: Colors.black, //枠線の色
width: 1, //枠線の太さ
style: BorderStyle.solid, //枠線の種類
),
),
),
child: Text(
buttonText,
style: const TextStyle(
fontSize: 24.0,
color: Colors.white,
),
),
),
);
}
flex
引数を使って、イコールボタンの幅を他のボタンの2倍にしています。color
引数はボタンの色を変えられるようにしています。
このコードを実行すると、以下のような画面が表示されます。
ボタンの間隔が広がり、イコールボタンが他のボタンの2倍の幅になりました。
5. カスタムウィジェットでボタンを共通化
現在のコードでは、ボタンを作成するたびに ElevatedButton
ウィジェットを記述しています。これをカスタムウィジェットとして共通化することで、コードの再利用性を高め、メンテナンスを容易にします。
dart
class CalculatorButton extends StatelessWidget {
const CalculatorButton({
super.key,
required this.text,
this.flex = 1,
required this.onPressed,
this.color = Colors.grey,
});
final String text;
final int flex;
final Color color;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return Expanded(
flex: flex,
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: color,
padding: const EdgeInsets.all(24.0),
shape: const RoundedRectangleBorder(
side: BorderSide(
color: Colors.black,
width: 1,
style: BorderStyle.solid,
),
),
),
child: Text(
text,
style: const TextStyle(
fontSize: 24.0,
color: Colors.white,
),
),
),
);
}
}
CalculatorButton
という名前の新しいステートレスウィジェットを作成しました。このウィジェットは、ボタンに表示するテキスト、フレックス値、ボタンの色、onPressed
コールバックを引数として受け取ります。
_buildButton
関数を削除し、CalculatorButton
ウィジェットを使うように変更します。
dart
Container(
height: 350.0,
color: Colors.black,
child: Column(
children: <Widget>[
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '7',
onPressed: () {},
),
CalculatorButton(
text: '8',
onPressed: () {},
),
CalculatorButton(
text: '9',
onPressed: () {},
),
CalculatorButton(
text: '/',
onPressed: () {},
),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '4',
onPressed: () {},
),
CalculatorButton(
text: '5',
onPressed: () {},
),
CalculatorButton(
text: '6',
onPressed: () {},
),
CalculatorButton(
text: 'x',
onPressed: () {},
),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '1',
onPressed: () {},
),
CalculatorButton(
text: '2',
onPressed: () {},
),
CalculatorButton(
text: '3',
onPressed: () {},
),
CalculatorButton(
text: '-',
onPressed: () {},
),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '.',
onPressed: () {},
),
CalculatorButton(
text: '0',
onPressed: () {},
),
CalculatorButton(
text: 'C',
onPressed: () {},
),
CalculatorButton(
text: '+',
onPressed: () {},
),
],
),
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CalculatorButton(
text: '=',
flex: 2,
onPressed: () {},
color: Colors.orange,
),
],
),
),
],
),
),
CalculatorButton
ウィジェットを使うことで、ボタン作成部分のコードがすっきりしました。
コラム:Flutterのウィジェットのライフサイクル
Flutterのウィジェットにはライフサイクルがあります。ステートフルウィジェットのライフサイクルは、以下のようになっています。
- createState(): ステートフルウィジェットが作成されると、
createState
メソッドが呼ばれ、State
オブジェクトが作成されます。 - initState():
State
オブジェクトが作成された直後に、initState
メソッドが呼ばれます。このメソッドは一度だけ呼ばれ、初期化処理などに使われます。 - didChangeDependencies():
initState
の後、および依存するオブジェクト(InheritedWidget
など)が変更されたときに、didChangeDependencies
メソッドが呼ばれます。 - build(): UIを構築するために、
build
メソッドが呼ばれます。このメソッドは、setState
が呼ばれた後など、頻繁に呼ばれる可能性があります。 - didUpdateWidget(): ウィジェットの設定が変更されたときに、
didUpdateWidget
メソッドが呼ばれます。 - setState():
State
オブジェクトの状態を変更し、UIを再構築するために、setState
メソッドを呼び出します。 - deactivate():
State
オブジェクトがウィジェットツリーから削除される前に、deactivate
メソッドが呼ばれます。 - dispose():
State
オブジェクトがウィジェットツリーから完全に削除されるときに、dispose
メソッドが呼ばれます。リソースの解放などに使われます。
これらのライフサイクルメソッドを適切にオーバーライドすることで、ウィジェットの状態変化に応じた処理を実装できます。