Flutterの勉強として電卓アプリ(計算機)を作ったので、その作り方を順を追って、全3回で説明していきます。
-
レイアウト作成1
-
2
-
3
私自身、初心者なのでコード等が少し雑かもしれませんが、出来る限り綺麗なコードを目指しました。
今回はレイアウト作成です。
特に難しいコードは書かないので、コードの説明はせず、Flutter Doc
JPへのリンクを貼り付けるだけにします。
(次回以降では説明します。)
# 環境
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel master, 1.19.0-2.0.pre.173, on Mac OS X 10.15.4 19E287, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.5)
[✓] Android Studio (version 3.6)
[✓] VS Code (version 1.45.1)
では始めていきます。
プロジェクトの作成
まず、flutterアプリーケーションを新規作成します。
名前はcalculator_appとしておきます
パッケージ名はそのままで
シュミレーターを起動
私はiPhone8を起動しました。
こんな感じで準備完了
適当に "Hello World"
*特に意味はない、やらなくていいです。
レイアウトを考える
必要なウィジェット
「電卓」に必要なウィジェットは大きく2つ
(1) 計算式を入力、答えの表示をするところ(赤枠)
(2) キーボード(青枠)
(3) キー(黄枠)
方針
(1) と (2) をColumn
(2) が (3) をchildrenとして持つようにします。
そして、それぞれのウィジェットを
(2) Keyboard
(3) Button
この時、
TextFiled は StatefullWidget
Keyboard, Button は StatelessWidget
を継承します。
コードを書く
それではコードを書いていきます。
まずは細かい内容は書かず、Widget build まで書きます。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
TextField(),
Keyboard(),
],
)
)
);
}
}
//=============================================================================
// 表示
class TextField extends StatefulWidget {
_TextFiledState createState() => _TextFiledState();
}
class _TextFiledState extends State<TextField> {
@override
Widget build(BuildContext context) {
// any code
}
}
//==============================================================================
// キーボード
class Keyboard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
// any code
);
}
}
// キーボタン
class Button extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
// any code
);
}
}
表示(TextFiled)のレイアウトを作る
確認用として「1+1」を表示させておきます。
コード
//==============================================================================
// 表示
class TextField extends StatefulWidget {
_TextFiledState createState() => _TextFiledState();
}
class _TextFiledState extends State<TextField> {
String _expression = '1+1';
@override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Container(
child: Align(
alignment: Alignment.centerRight,
child: Text(
_expression,
style: TextStyle(
fontSize: 64.0,
),
),
),
)
);
}
}
シュミレーターの方はこのように表示されました。
キーボード(Keyboard, Button)のレイアウトを作る
ここでは Keyboard に GridView を使います。
KeyboarderのContainerに色(#87cefa)をつけ、Buttonの Container には色をつけません。
ButtonにはFlatButtonを使います。
コード
//==============================================================================
// キーボード
class Keyboard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Expanded(
flex: 2,
child: Center(
child: Container(
color: const Color(0xff87cefa),
child: GridView.count(
crossAxisCount: 4,
mainAxisSpacing: 3.0,
crossAxisSpacing: 3.0,
children: [
'7', '8', '9', '÷',
'4', '5', '6', '×',
'1', '2', '3', '-',
'C', '0', '=', '+',
].map((key) {
return GridTile(
child: Button(key),
);
}).toList(),
),
)
)
);
}
}
// キーボタン
class Button extends StatelessWidget {
final _key;
Button(this._key);
@override
Widget build(BuildContext context) {
return Container(
child: FlatButton(
child: Center(
child: Text(
_key,
style: TextStyle(fontSize: 46.0),
),
),
)
);
}
}
シュミレーターの方はこのように表示されました。
レイアウト完成
ここまでで電卓のレイアウトは完成しました。
ここにmain.dartのコード全体を載せておきます。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
TextField(),
Keyboard(),
],
)
)
);
}
}
//==============================================================================
// 表示
class TextField extends StatefulWidget {
_TextFiledState createState() => _TextFiledState();
}
class _TextFiledState extends State<TextField> {
String _expression = '1+1';
@override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Container(
child: Align(
alignment: Alignment.centerRight,
child: Text(
_expression,
style: TextStyle(
fontSize: 64.0,
),
),
),
)
);
}
}
//==============================================================================
// キーボード
class Keyboard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Expanded(
flex: 2,
child: Center(
child: Container(
color: const Color(0xff87cefa),
child: GridView.count(
crossAxisCount: 4,
mainAxisSpacing: 3.0,
crossAxisSpacing: 3.0,
children: [
'7', '8', '9', '÷',
'4', '5', '6', '×',
'1', '2', '3', '-',
'C', '0', '=', '+',
].map((key) {
return GridTile(
child: Button(key),
);
}).toList(),
),
)
)
);
}
}
// キーボタン
class Button extends StatelessWidget {
final _key;
Button(this._key);
@override
Widget build(BuildContext context) {
return Container(
child: FlatButton(
child: Center(
child: Text(
_key,
style: TextStyle(fontSize: 46.0),
),
),
)
);
}
}
現在シュミレータは次のようになっています。
まとめ
今回で電卓のレイアウトが完成しました。
次回はKeyboardのボタン入力、TextFiledへの表示を実装していきます。

![[PIC 1-1]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvXrw6P0gDWsdHuu9RMgZLpLNLbRDvhHcWHVsVZw_UGrHbSLAEyyna8FV2vAFgFDHmLdjK-LWq1VjtIkC-WnU9XmXRVBwowIg2OYvTr6plUhQ4IgLaDIypBLe9bnh8JUqScFLAWgJoM5k/w640-h478/1-1.png)
![[PIC 1-2]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit8QG0xXNb169ozurmINSyiBbWaW2Eupz-RTePUsVc6p80m6ZuBp_kq5C2DB-vI4NVV8zzipiLKJ7EKKJVG7bpCmyOcatxQK_yiOBytc2qVrNpNgFn75Loi8f9GmIheX9Irk5AF7NUjIc/w640-h478/1-2.png)
![[PIC 1-3]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRia2vAouFXtUtukyRjfpeRHUUT9kbtdEGPYA_F3pAgeXXd_hmwSApBZrgj3xNoph3BIByWUo_URLh-B9kpWdbB1EhyRWTnSS22Lmx1g-Mu1OnUSm-PEWE-1S-swUB6q2t0K_PimA4blU/w640-h478/1-3.png)
![[PIC 1-4]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGLG2urummH28Slh7-vGYDhO0TUTCU_1CG20Tt69nS1YMQemhN_DbLlYGxYqzAnEUv8xLNmqM3mSr2Rqb-5yob9g0UDKiLg3L03SdmhZGP56JQsVQAXDyHwEqOvQX_lBX6rDI2lDaQX-I/w640-h398/1-4.png)
![[PIC 1-5]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiULvYjA5G7KS5Z3zX1xZAijBCWX-h3fR2dBoM_-7XlFMvkq_Sb4TgCX56Bk4oYW7f24ztu6jL41XPECUiRBssSIU2O76aphw1ccnAv0qIoToJOJ6Lu8lbPxxcFEoWqgzJJaSP6XEou_io/w640-h298/1-5.png)
![[PIC 1-6]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqagEmsIvC5RXUUmBt0Ag5PAkby8y_J9cDHgJl7InEbhjeXayLSBs8mf9h9WLtKsHUfTBViE-9x_4POO5pNYUqwjgHhCwnU5W2oKDnRIeja-C0cpF0bqbdvqWzmIGknm50TOlLZRzCCWs/w640-h392/1-6.png)
![[PIC 1-7]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcLEGmjSPHAJvSynHvr_fPv546zXLBc-j2cBxBxjtDWNtO4HjouEEoKffZq1Dim4xK7erbSH3EjspkOqTavDRHhMmlsNuHr9eGQZoJKBpJbtf3ntTaaGuwXkskc-7yabiTC6Kx_s0ob_Y/w640-h392/1-7.png)
![[PIC 2-1]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjcmxJV9abcH12V7cLd9WlJ5VUU2SMUO3t7B-a7PPro-hpBZ1DNoQdTrBYNW-2ZjV3_qm752gKc0ZDuogAKAJ3l7kNWQd1OvsbpUMKd6YBGaQo-VGH5sSReiYXya7RURsVdhVdKib7RfY/w400-h324/2-1.png)
![[PIC 3-1]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLW14bTyWyKWXtDQZAcIBk-A2sT6jRlTG9d1q8q65hyphenhyphenvu5zjiHKPyyQHeFAG5KXQAGbT69SCR6EPo9hgQ7ytBEgBrijMDAr90IUazcMOwaGeTA80XbaCe2TN2BCpIHhtOZlNAiGPfeJAU/w295-h400/3-1.png)
![[PIC 4-1]](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPeMJX9_KI1uh-suqD29kQULAWFttPQubj_OgbnTmdUfhaadIg41ZktEhyVf4Qb62so2LbgBnBQqewPqv9LcLByg5LDddmrMHMJcQZ3dAvD8eqG9dkmcybT6vYLzoQVbPJKEPc2v_gV5U/w302-h640/4-1.png)
0 件のコメント:
コメントを投稿