💡

【Flutter】TextEditingController使ってTextFieldに初期値を 持たせて表示する

2022/02/15に公開

はじめに

今回は以下動画を参考にして自分なりに作ってみたサンプルとその説明になります。
TextEditingController使った方法は動画内の該当箇所からわかりやすく説明されてますので、
こちらも参考にしてみてください。
YouTubeのvideoIDが不正ですhttps://youtu.be/BY4x8FIj0PY?t=681

この記事がどなたかの参考になれば幸いです。
また誤り等ございましたらご指摘いただけると幸いです。

作ったもの

今回はtodo一覧画面で表示されているtodoを編集画面で編集したいといった場合、
一覧画面で表示されているtodoを編集画面のTextFieldにそのまま表示するといったケースを想定してTodoアプリっぽいものを作ってみました。
Providerで状態管理を行い、UIとロジックを分けてファイルを作ってます。
一覧ページでtodo一覧を表示し、編集アイコンをタップすると編集画面に
移動→一覧ページのtodoがTextFieldに入った状態で表示されます。(実際に編集はできません)

環境

MacOS 12.1
Flutter (Channel stable, 2.10.0)
Dart SDK version: 2.16.0 (stable)
provider: ^6.0.2

ファイル一覧

  • main.dart
    todo一覧表示画面
  • main_model.dart
    一覧画面の状態管理
  • edit_todo_page.dart
    todo編集画面
  • edit_todo_model.dart
    編集画面の状態管理
  • todo.dart
    todoのオブジェクトTodoを定義しているファイル

実際のアプリ

Simulator Screen Recording - iPhone 13 - 2022-02-14 at 18.00.20.gif

各ファイルでの作業の流れ

1. main.dart

  • Todo(ListTileに表示されるtodoのtitleを持っているクラス)を画面遷移で使うNavigator.pushで使われるeditTodoPage()の引数でわたす

2. edit_todo_page.dart

  • Todoをコンストラクタで受け取って初期化する
  • ChangeNotifierProviderでcreateされるmodel(EditTodoModel)の引数でTodoをedit_todo_modelにわたす

3. edit_todo_model

  • Todoを引数にして初期化処理をする。edit_todo_pageのTextFieldで使うTextEditingControllerの初期化もこのコンストラクタでやっておく。

各ファイルの説明

main_model.dart

TodoをListViewで表示するため以下のような感じで用意してます。

main_model.dart
import 'package:flutter/material.dart';
import 'package:todoapp/todo.dart';

class MainModel extends ChangeNotifier {
  // todoListを手動で作成。ListViewで表示するためListにしておく。
  List<Todo> todoList = [Todo('トイレ掃除'), Todo('犬の散歩')];
}

todo.dart

Todoを定義。ListTileで表示されるtitleを持っている。このtilteをedit_todo_pageのTextFieldで
表示したい

todo.dart
class Todo {
  Todo(this.title);
  String title;
}

main.dart

  1. main.dart→edit_todo_pageへentitiy(Todoオブジェクト)をわたしたいので、
    edit_todo_pageへの画面遷移時に使うNavigator.pushで渡すedit_book_pageのコンストラクタの引数にTodo todoを入れる。

todoListはmain_model.dartで持っているTodoのリスト。

main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todoapp/main_model.dart';

import 'edit_todo/edit_todo_page.dart';

Future<void> main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TODO アプリ',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MainPage(),
    );
  }
}


class MainPage extends StatelessWidget {
  const MainPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<MainModel>(
      create: (_) => MainModel(),
      child: Scaffold(
        appBar: AppBar(
          title: const Text('TextFieldに初期値を持たせるサンプル'),
        ),
        body: Consumer<MainModel>(
          builder: (context, model, child) {
            final todoList = model.todoList;
            return ListView(
              children: todoList
                  .map(
                    (todo) => ListTile(
                      trailing: IconButton(
                        icon: const Icon(Icons.edit),
                        onPressed: () async {
                          //Todo :画面遷移
                          await Navigator.push(
                            context,
                            MaterialPageRoute(
                              // 1. 遷移先の編集画面EditTodoPage()にTodoをわたす
                              builder: (context) => EditTodoPage(todo),
                            ),
                          );
                        },
                      ),
                      title: Text(todo.title),
                    ),
                  )
                  .toList(),
            );
          },
        ),
      ),
    );
  }
}

edit_todo_page.dart

  1. edit_book_pageのconstractorに引数にTodoを持たせておく。
  2. main.dartからTodoを受け取るため、変数を用意する。
    (これでこのpageは必ず、引数にTodoを入れないと使えなくなる。)
  3. ChangeNotifierProviderでcreateに入れるmodelのコンストラクタの引数にTodoを入れる
  4. TextFieldのcontrollerにmodelのTextEditingControllerを入れる
edit_todo_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import '../todo.dart';
import 'edit_todo_model.dart';

class EditTodoPage extends StatelessWidget {
  // 1. Todoを受け取るため、constructorの引数に指定しておく。
  const EditTodoPage(this.todo, {Key? key}) : super(key: key);
  // 2. Todoを受け取るための変数を用意。
  final Todo todo;

  
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<EditTodoModel>(
      // 3. 受け取ったTodoをさらにモデルにわたす。
      create: (_) => EditTodoModel(todo),
      child: Scaffold(
        appBar: AppBar(
          title: const Text('編集画面'),
        ),
        body: Consumer<EditTodoModel>(builder: (context, model, child) {
          return Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                TextField(
                  // 4. TextEditingControllerのtextはmodelで初期化されているので、
                  // main.dartのtextを持っている。
                  // そのため画面が描画されたときにtextが表示される。
                  controller: model.todoEditTextEditingController,
                  onChanged: (text) {
                    // 入力欄でテキストが編集された時にやること。
                  },
                ),
                const SizedBox(
                  height: 16,
                ),
                ElevatedButton(
                  onPressed: () {
                    // 入力されたtextを元にデータベースを更新する処理などをやる
                  },
                  child: const Text('編集'),
                )
              ],
            ),
          );
        }),
      ),
    );
  }
}


edit_book_model

  1. Todoを受け取るためにconstructorで初期化処理。
  2. constructorで初期化するためのTodoも定義しておく。
  3. edit_todo_page.dartでTextFieldで使うためのTextEditingControllerを定義する。
edit_book_model
import 'package:flutter/material.dart';
import 'package:todoapp/todo.dart';

class EditTodoModel extends ChangeNotifier {
  // 1.  Todoを受け取るためにconstructorで初期化処理。
  EditTodoModel(this.todo) {
    // TextEditingControllerのtextにTodoの持っているtitleを入れる
    todoEditTextEditingController.text = todo.title;
  }

  // 2. constructorで初期化するためのTodoを定義しておく。
  Todo todo;

  //  updateの時に入力されるtext用に用意
  String? updateTodoText;

  // 3. TextFieldで入力されたtextを扱うためのTextEditingController
  final TextEditingController todoEditTextEditingController =
      TextEditingController();

  // TextEditingControllerをdisposeする。
  
  void dispose() {
    todoEditTextEditingController.dispose();
    super.dispose();
  }
}

参考サイト

https://docs.flutter.dev/cookbook/forms/retrieve-input
https://zenn.dev/kboy/books/ca6a9c93fd23f3/viewer/14dfcb

最後に

ここまで読んでいただきありがとうございました!

Discussion

ログインするとコメントできます