こんにちは、Flutterの世界への扉を開きましょう!今日は、中学生でも理解できるように、Flutterでシンプルな画像ギャラリーアプリを作る方法を一緒に学んでいきましょう。このアプリでは、画像のURLを入力して、画面上に表示する画像を追加・変更できます。それでは、まずプログラム全文から見ていきましょう。
・アプリのUI作成方法がわからない。
・FloatingActionButtonの使い方がわからない。
・画像を表示するアプリを作ってみた。
今回はこんな疑問を解決します。
・シンプルな画像を表示するアプリを作成することができる。
・FloatingActionButtonの使い方を理解できる。
・画面のUIを作成することができる。
まず初めに、プログラム全文を記載します。これをエミュレータで実行すると、図のようなアプリが起動されます。起動方法はCtrl+Shift+pで起動できます。詳細は下記記事を参照してください。

プログラムの紹介
このプログラムをまず実行してみてください。
import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  _MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
  List<String> imageUrls = [
    'https://i.imgur.com/372zsV3.jpg',
    'https://i.imgur.com/TcaEu6j.jpg',
  ];
  int currentIndex = 0;
  // 新しい画像URLを入力するためのコントローラ
  final TextEditingController _urlController = TextEditingController();
  void _changeImage() {
    setState(() {
      currentIndex = (currentIndex + 1) % imageUrls.length;
    });
  }
  void _addImageUrl() {
    final String newUrl = _urlController.text;
    if (newUrl.isNotEmpty) {
      setState(() {
        imageUrls.add(newUrl); // 入力されたURLをリストに追加
        _urlController.clear(); // テキストフィールドをクリア
      });
    }
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        resizeToAvoidBottomInset: true, // キーボードでコンテンツが隠れないようにする
        appBar: AppBar(
          title: const Text('Image Gallery'),
        ),
        body: SingleChildScrollView( // スクロール可能にする
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
              if (imageUrls.isNotEmpty)
                Image.network(imageUrls[currentIndex]),
              ElevatedButton(
                onPressed: _changeImage,
                child: const Text('Change Image'),
              ),
              // URL入力フィールドと追加ボタンを含むRowウィジェット
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 20),
                      child: TextField(
                        controller: _urlController,
                        decoration: const InputDecoration(
                          hintText: 'Enter image URL',
                        ),
                      ),
                    ),
                  ),
                  FloatingActionButton(
                    onPressed: _addImageUrl,
                    child: const Icon(Icons.add),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    ),
    );
  }
}こんな画面が表示されます。Changeを押すと画像が切り替わります。”Enter Image URL”をクリックすると、

キーボードが表示され、自由に入力できるようになります。ここに画像URLを打ち込み、+ボタンを押すと、画像URLが追加され、Change Imageボタンを押すと、追加された画像が表示されます。

ではプログラムの内容について紹介していきます。
プログラムの解説
FloatingActionButtonとは?
FloatingActionButtonは画面上に浮かぶように表示されるボタンです。このボタンをタップすると、何らかのアクションを実行できます。アプリでよく見かける+ボタンのことですよ。これです。

FloatingActionButton(
  onPressed: _addImageUrl,
  child: const Icon(Icons.add),
),上のコードでは、_addImageUrlという関数を呼び出しています。この関数は、テキストフィールドに入力された新しい画像のURLをリストに追加する役割を持っています。
つまり、_addImageURLで画像URLを追加したら、その画像が追加されるようになっています。
こんな感じで、URLを追加します。

+を押して、Change Imageを押すと、追加されたURLの画像が表示されます。

TextEditingControllerを使ってみよう
テキストフィールドの入力内容を管理するためにTextEditingControllerを使います。このコントローラを使うと、入力されたテキストの値を取得したり、テキストフィールドをクリアしたりできるんです。
final TextEditingController _urlController = TextEditingController();これを使えるようになります。

アプリのUIを作成する
Scaffoldウィジェットを使って、アプリのベースとなるUIを作成します。アプリバー(上部のバー)、ボディ(中央のコンテンツ部分)、フローティングアクションボタンを配置しています。
Scaffold(
  appBar: AppBar(
    title: const Text('Image Gallery'),
  ),
  body: SingleChildScrollView(
    // 中央のコンテンツ
  ),
  floatingActionButton: FloatingActionButton(
    // フローティングアクションボタン
  ),
),SingleChildScrollViewを使うと、キーボードが表示されても内容がスクロールできるようになり、画面がはみ出ることを防げます。
_addImageUrl関数の内容
_addImageUrlメソッド全体の動作
_addImageUrlメソッドは、ユーザーが入力したテキストフィールドの内容(この場合は画像のURL)を取得し、それを画像のURLリストに追加するための関数です。このメソッド内で使われている_urlController.textと.addメソッド、そしてインスタンス化について詳しく見ていきましょう。
void _addImageUrl() {
  final String newUrl = _urlController.text; // テキストフィールドからURLを取得
  if (newUrl.isNotEmpty) { // URLが空でないことを確認
    setState(() {
      imageUrls.add(newUrl); // URLをリストに追加
      _urlController.clear(); // テキストフィールドをクリア
    });
  }
}この関数は以下のステップで動作します:
- _urlController.textを使用してテキストフィールドからURLを取得し、newUrl変数に格納します。
 - if (newUrl.isNotEmpty) {…}で、ユーザーが何かテキストを入力したか確認します。何も入力されていなければ、リストには何も追加されません。
 - setState()関数を呼び出して、ウィジェットの状態を更新します。setStateは、UIが更新されるようにFlutterに伝えるために使用されます。
 - imageUrls.add(newUrl)で新しいURLをimageUrlsリストに追加します。
 - _urlController.clear()でテキストフィールドの内容をクリアし、次の入力のために準備します。
 
このメソッドを使用することで、ユーザーはアプリに新しい画像URLを追加し、その画像を表示することができるようになります。
なので、画像を追加すると、複数の画像に変更することができます。

Changeを押すと変わります。

補足
currentIndex = (currentIndex + 1) % imageUrls.length;ってなに?
このコード % imageUrls.length における % は、モジュロ演算子、または剰余演算子と呼ばれるものです。モジュロ演算子は、左側の数値を右側の数値で割った際の余りを返します。この演算子は、特にリストのインデックスを循環させる際に便利です。
例えば、imageUrls が画像のURLを格納したリストで、currentIndex が現在表示している画像のインデックスを保持しているとしましょう。currentIndex + 1 で次の画像のインデックスに移動しようとしている場合、imageUrls.length でリストの長さ(つまり、画像の数)を取得します。
currentIndex = (currentIndex + 1) % imageUrls.length;このコードの意味は次の通りです:
- currentIndex + 1 で次の画像のインデックスに移動します。
 - % imageUrls.length でその数値をリストの長さで割った余りを求めます。
 
これにより、currentIndex がリストの最後のインデックスを超えた場合(例えば、リストの長さが3で、currentIndex が2のときに1を加えると3になりますが、リストのインデックスは0から始まるため最大は2)、余りが0になりリストの最初に戻ります。つまり、画像のリストを循環させることができます。
このようにして、currentIndex がリストの範囲内で常に循環するように保証され、リストの最後の画像の次に再び最初の画像が表示されるようになります。これは、ギャラリーアプリやスライドショーなどでよく使われるテクニックです。
final ってなに?
finalキーワードは、Dart(Flutterのプログラミング言語)において、変数が一度値がセットされた後は変更不可能であることを示すために使用されます。つまり、finalで宣言された変数は、初期化後に再代入することができません。
例えば、
final String newUrl = _urlController.text;この行において、newUrlはTextEditingController _urlControllerの現在のテキスト値で初期化されます。この時点から、newUrlはその値を保持し続け、プログラムのどの部分でも変更することはできません。
finalを使用する理由は以下の通りです:
- 不変性(Immutability)を保証する: 不変性はバグを減らし、コードの可読性と保守性を向上させるための重要なプログラミングの概念です。
final変数は一度だけ設定でき、その後は変更不可能なため、意図せずに値が変更されることを防ぎます。 - コンパイラの最適化: 不変の変数はコンパイラによる最適化の対象となり得ます。コンパイラは値が変わらないことを知っているため、パフォーマンスを向上させるための最適化を行うことができます。
 - プログラムの意図を明確にする: 
finalを使用することで、他の開発者に対してこの変数は初期化後に変更されないことを明示的に伝えることができます。これにより、コードの意図がより明確になります。 - リソース管理: 特にFlutterのウィジェットのライフサイクルにおいて、ウィジェットが再構築されるたびに変数が変更されるべきではない場合(例えば、ウィジェットが持つデータモデル)に
finalを使用します。 
簡単に言うと、finalは「この変数は一度設定されたら変わりませんよ」と宣言するために使用されるキーワードです。これにより、プログラムがより予測可能で安全になります。
最後に
このブログを通じて、FloatingActionButtonの追加方法、TextEditingControllerの使い方、そしてFlutterでの基本的なUI作成方法について理解を深めていただければと思います。プログラミングは難しそうに見えるかもしれませんが、基本から一歩ずつ学べば、きっと楽しくなるはずです。今回のコードを自分のエディタにコピーして、実際に手を動かしてみましょう!

											




							
							
							
															
							
							
							
															
							
							
							
															

										
					
									
										
										
										
																	
										
										
										
																	
										
										
										
																	
										
										
										
																	
										
										
										
																	
										
										
										
																	
										
										
										
																	
										
										
										
																	
										
										
										
																	
										
										
										
																	