1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

知識ほぼゼロから始めるアプリ開発録② ChatGPTに頼らずに頑張ってみる

1
Last updated at Posted at 2026-05-28

今日は、自作アプリのイマイチなUIを向上させる作業をしていきます。

ChatGPTは禁止

今まではChatGPTに指示して
コードを修正してもらってコピペするだけでしたが
勉強の成果を試すため、自力で頑張ってみたいと思います。

お題は、かなり前にテキトーに作ってほったらかしていた
Flutter_UI_Catalogです。
新しい画面を追加する作業をしております。

このように
Widgetごとにカードが表示されているのですが
右上のアイコンが小さいし
上じゃなくて真ん中に表示したい。

ちなみにこのアイコンは、そのWidgetの詳細ページから
Touchモードに移行できることを示すためにつけようと思っているものです。

まず、現在のコード(一部)はこちら

@override
  Widget build(BuildContext context) {
    if (data == null) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    }

    final keys = data!.keys.toList();

    return Scaffold(
      appBar: AppBar(title: const Text('Learn')),
      body: ListView.builder(
        padding: const EdgeInsets.all(12),
        itemCount: keys.length,
        itemBuilder: (context, index) {
          final key = keys[index];

          // _meta を一覧から除外
          if (key == '_meta') {
            return const SizedBox.shrink();
          }

          final widgetData = data![key];

          final category =
          (widgetData['category'] ?? '').toString();

          // Touch対応
          final touch =
              widgetData['touch'] ?? false;

          // Param数
          final paramCount =
              (widgetData['params'] as List?)?.length ?? 0;

          return Card(
            margin: const EdgeInsets.symmetric(vertical: 6),

            child: ListTile(
              leading: Icon(
                _getCategoryIcon(category),
                color: Colors.black54,
              ),

              title: Row(
                children: [

                  Expanded(
                    child: Text(
                      key,
                      style: const TextStyle(
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ),

                  // Touchアイコン
                  if (touch)
                    const Padding(
                      padding: EdgeInsets.only(right: 8),
                      child: Icon(
                        Icons.touch_app,
                        size: 20,
                        color: Colors.orange,
                      ),
                    ),
                ],
              ),

              // Param数バッジ
              subtitle: Padding(
                padding: const EdgeInsets.only(top: 6),

                child: Row(
                  children: [

                    Container(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 10,
                        vertical: 4,
                      ),

                      decoration: BoxDecoration(
                        color: Colors.white.withOpacity(0.7),
                        borderRadius:
                        BorderRadius.circular(999),
                      ),

                      child: Text(
                        '$paramCount params',

                        style: const TextStyle(
                          fontSize: 12,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ],
                ),
              ),

              trailing: const Icon(
                Icons.arrow_forward_ios,
                size: 16,
              ),

              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (_) => WidgetDetailPage(
                      name: key,
                      data: widgetData,
                    ),
                  ),
                );
              },
            ),
          );
        },
      ),
    );
  }

修正箇所を特定する

さて、脱初心者を目指し日々勉強している私ですが
画面の見た目をいじりたかったら
Scaffold()の中を変更すれば良いのかな?
ということは分かってきました。

そんなわけでScaffold()で検索してみます。

なんと2か所該当してしまいました。8行目と15行目です。

どちらを直せばよいのだろう?

上のScaffold()If(){}で囲まれているし
中身を見るとCircularProgressIndicator()しか無い。
これって確か、読み込み中を示すぐるぐるを表示させるやつだったはず。

テストでこの画面を何回も起動しており
ここで読み込み中になったことはなかったので、一瞬戸惑いましたが
どうやら本丸はここではなく、その下のScaffold()のようです。


というわけで、2つ目のScaffold()の中を見ていきます。
私が今理解できる範囲で、Widgetと、名前付き引数を
整理してみました。
(本来のツリーの使い方とは違うかもしれません。間違っているところもあるかも)

Scaffold
├ appBar:AppBar
│     └ title:Text()
└ body:ListView.builder()
        ├ padding:EdgeInsets.all()
        ├ itemCount:keys.length
        └ itemBuilder:(){}

一部省略しましたが
構造としては意外とシンプル。
そして、このitemBuilder:(){}がやっかいですね。
これはおそらく、Widgetを返す関数というやつではないでしょうか。
そしてどうやら、画面を作っている部品の大半はここに入っている模様。

頑張ってさらに中を読み解いてみます。
最初のIfやfinal群は、構造には関係なさそうなので無視。
すると、return Card()を発見!
修正箇所はここですね。

下までスクロールしてみても
この後のコードは全て、このCardの中身として書かれている様子。
このCardが、itemBuilder:(){}が返すWidgetに間違いなさそうです。


では、Cardの中身を見ていきましょう。
これは1つづつ辿っていては大変なので
Icon()を狙い撃ちします。

Icon()は全部で3か所。

child: ListTile(
              leading: Icon(
                _getCategoryIcon(category),
                color: Colors.black54,
              ),

ここは、categoryを渡しているので
カード左端に表示されている、Widgetのカテゴリーを示すアイコンでしょう。

次はここ。

                  if (touch)
                    const Padding(
                      padding: EdgeInsets.only(right: 8),
                      child: Icon(
                        Icons.touch_app,
                        size: 20,
                        color: Colors.orange,
                      ),
                    ),

どう考えてもここですね。
念のためもう一つも見てみます。

trailing: const Icon(
                Icons.arrow_forward_ios,
                size: 16,
              ),

違いますね。これは右端の>のことでしょう。

こんな感じで、直すべき場所は絞れました。
あとはどう直すか。
サイズは簡単に直せそうだけど、上下真ん中に配置はどうするのかな。

調べて、考えて、やってみる。

普段なら即ChatGPTに聞いてコピペですが
今日は頑張って自力で調べてみます。

どうにかFlutterDocsのLayout widgetsのページにたどり着きました。
この中だと、Centerというのを使えば良さそう。

しかし説明を見てもさっぱりなので(英語は苦手です)
いろいろ実験してみました。

まずはconst Padding(
を、const Center(
に変更してみます。

すると、その下のpaddingのところがエラーになります。
FlutterDocsのCenterのページを見ると
Propertiesの中にpaddingというのはありません。
だからエラーになったのでしょう。
思い切ってこの行は削除してみます。

そしてRun。

なぜか全然変化無し。
そこでアイコンサイズを40に変えてみます。
すると

どうやら、このアイコンの下に見えない壁がある模様。

埒が明かないので元に戻して仕切り直しです。

もう一度コードを見直してみると
まずListTileがあって、その中に

  • title:
  • subtitle:

があって、今いじっているIconは
title:の中に、Row()で横並びに入っているみたいです。

ListTileのドキュメントを見ると

  • leading:
  • title:
  • subtitle:
  • trailing:

このあたりの引数で、表示位置が決まっているみたいです。
こんな感じですね。

titleやsubtitleの入っているブロックを
まとめて分割する方法は思いつきません。

では、trailingに
Row()でIcon()入れたら良いのでは?

こうしてみました。

trailing: Row(
                children: [
                  if (touch)
                    const Padding(
                      padding: EdgeInsets.only(right: 8),
                      child: Icon(
                        Icons.touch_app,
                        size: 20,
                        color: Colors.orange,
                      ),
                    ),

                  const Icon(
                    Icons.arrow_forward_ios,
                    size: 16,
                  ),
                ],
              ),

結果、
クラッシュしました

どうやらここまでのようです。
諦めてChatGPTに修正を頼みます。

問題点は2か所

① Expanded

title: Expanded( 
    child: Text(
        key,
        style: const TextStyle(
         fontWeight: FontWeight.bold,
         ),
      ),
    ),

Expanded は

  • Row
  • Column
  • Flex

の子としてしか使えません!

② trailing の Row

trailing: Row(

Row はデフォルトで「できるだけ横幅を広げよう」とします。

でも trailing は
「最小サイズで表示して」
という場所。

なので衝突します。
こうすればOK。

trailing: Row(
  mainAxisSize: MainAxisSize.min,

無事、修正できました。

ちょっと勉強しただけで
分かったつもりになって背伸びしたら
あっさり撃沈しました。

まだまだ学ばなければならないことは多いみたいです。

それでも、直すべき箇所は合ってたみたいだから
前よりは成長しているはず。

懲りずにまた挑戦してみたいと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?