Flutterの勉強のために作成したサンプルアプリ

Flutterの勉強のために作成したサンプルアプリをアップロード。


[初期画面]


[データ登録]           [データ一覧]


[データ更新]           [データ削除]

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '血圧データ管理',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: '血圧データ管理'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({Key? key, required this.title}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool searchFlag = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        backgroundColor: Colors.blue,
        title: Text(widget.title),
      ),
      body: Container(
        color: Colors.greenAccent,
        child: Center(
          child: Column(
            children: <Widget>[
              Container(
                  color: Colors.greenAccent,
                  width: double.infinity,
                  height: 30,
                  margin: const EdgeInsets.all(10.0),
                  child: Center(
                      child: Row(children: <Widget>[
                    const Text('表示期間: '),
                    Expanded(child: createTextFieldSearchStartDate()),
                    const Text('~'),
                    Expanded(child: createTextFieldSearchEndDate()),
                    const Text('      '),
                    ElevatedButton(
                        style: ElevatedButton.styleFrom(
                            backgroundColor: Colors.blue,
                            elevation: 10,
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(30.0),
                            )),
                        onPressed: _handlePressedSearchButton,
                        child: const Text('検索')),
                  ]))),
              Expanded(child: createSearchList(context)),
              Container(
                  height: 90,
                  decoration: BoxDecoration(
                      color: Colors.grey, border: Border.all(width: 3)),
                  child: Column(children: <Widget>[
                    Container(
                        color: Colors.grey,
                        height: 25,
                        margin: const EdgeInsets.all(5.0),
                        // padding: const EdgeInsets.all(10.0),
                        child: Row(children: <Widget>[
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 50,
                            child: const Text(' 測定日'),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 100,
                            child: createTextFieldKeisokuDate(),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 65,
                            child: const Text(' 測定時刻'),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 65,
                            child: createTextFieldKeisokuTime(),
                          ),
                        ])),
                    Container(
                        color: Colors.grey,
                        height: 25,
                        margin: const EdgeInsets.all(10.0),
                        // padding: const EdgeInsets.all(10.0),
                        child: Row(children: <Widget>[
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 50,
                            child: const Text(' 血圧値'),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 55,
                            child: createTextFieldUpperValue(),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 10,
                            child: const Text('/'),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 55,
                            child: createTextFieldLowValue(),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 50,
                            child: const Text(' 脈拍数'),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 55,
                            child: createTextFieldShinpakuValue(),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 20,
                            child: const Text(' '),
                          ),
                          Container(
                            color: Colors.grey,
                            height: 25,
                            width: 80,
                            child: ElevatedButton(
                                style: ElevatedButton.styleFrom(
                                    backgroundColor: Colors.blue,
                                    elevation: 10,
                                    shape: RoundedRectangleBorder(
                                      borderRadius: BorderRadius.circular(20.0),
                                    )),
                                onPressed: _handlePressedRegButton,
                                child: const Text('登録')),
                          ),
                        ])),
                  ]))
            ],
          ),
        ),
      ),
    );
  }

  Widget createSearchList(BuildContext context) {
    if (searchFlag == false) {
      return (Container(
        color: Colors.blue,
      ));
    }

    KetsuatsuNisshiDao dao = KetsuatsuNisshiDao();
    String s1 = _textControllerSearchStartDate.text;
    String s2 = _textControllerSearchEndDate.text;

    return (FutureBuilder<List<Map<String, dynamic>>>(
      future: dao.search(s1, s2),
      builder: (ctx, dataSnapshot) {
        if (dataSnapshot.connectionState == ConnectionState.waiting) {
          return const Center(
            child: Text('検索中'),
          );
        }

        if (dataSnapshot.error != null) {
          return const Center(child: Text('エラーがおきました'));
        }

        List<Map<String, dynamic>>? list = dataSnapshot.data;
        return (ListView.builder(
          itemCount: list?.length,
          itemExtent: 30,
          itemBuilder: (context, index) {
            Map m = list![index];
            String s1 = m['KeisokuDate'];
            String s2 = m['KeisokuTime'];
            String s3 = m['UpperValue'].toString();
            String s4 = m['LowValue'].toString();
            String s5 = m['ShinpakuValue'].toString();

            String s11 =
                '${s1.substring(0, 4)}/${s1.substring(4, 6)}/${s1.substring(6)}';
            String s22 = '${s2.substring(0, 2)}:${s2.substring(2)}';
            String s9 = "$s11 $s22 $s3/$s4/$s5";

            return (Container(
                color: index % 2 == 0 ? Colors.lime : Colors.grey,
                height: 30,
                child: Row(children: <Widget>[
                  const Expanded(flex: 1, child: Text('')),
                  Expanded(flex: 12, child: Text(s9)),
                  Expanded(
                      flex: 4,
                      child: Container(
                        margin: const EdgeInsets.all(2),
                        child: ElevatedButton(
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.blue,
                              elevation: 10,
                            ),
                            onPressed: () async {
                              final String? result =
                                  await DialogUtils.showDataUpdateDialog(
                                      context,
                                      KetsuatsuNisshiBean.fromMap(list[index]));
                              if (result != null && result == 'OK') {
                                setState(() {});
                              }
                            },
                            child: const Text('更新')),
                      )),
                  Expanded(
                      flex: 4,
                      child: Container(
                        margin: const EdgeInsets.all(2),
                        child: ElevatedButton(
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.red,
                              elevation: 10,
                            ),
                            onPressed: () async {
                              final String? result =
                                  await DialogUtils.showDataDeleteDialog(
                                      context,
                                      KetsuatsuNisshiBean.fromMap(list[index]));
                              if (result != null && result == 'OK') {
                                setState(() {});
                              }
                            },
                            child: const Text('削除')),
                      ))
                ])));
          },
        ));
      },
    ));
  }

  void _handlePressedSearchButton() {
    setState(() {
      searchFlag = true;
    });
  }

  void _handlePressedRegButton() {
    String s1 = _textControllerKeisokuDate.text;
    String s2 = _textControllerKeisokuTime.text;
    String s3 = _textControllerUpperValue.text;
    String s4 = _textControllerLowValue.text;
    String s5 = _textControllerShinpakuValue.text;

    KetsuatsuNisshiBean ketsuatsuNisshiBean = KetsuatsuNisshiBean(
        0, 1, s1, s2, int.parse(s3), int.parse(s4), int.parse(s5), '');
    KetsuatsuNisshiDao dao = KetsuatsuNisshiDao();
    dao.insert(ketsuatsuNisshiBean);
    _textControllerKeisokuDate.text = '';
    _textControllerKeisokuTime.text = '';
    _textControllerUpperValue.text = '';
    _textControllerLowValue.text = '';
    _textControllerShinpakuValue.text = '';
  }

  final TextEditingController _textControllerSearchStartDate =
      TextEditingController();
  final TextEditingController _textControllerSearchEndDate =
      TextEditingController();

  final TextEditingController _textControllerKeisokuDate =
      TextEditingController();

  final TextEditingController _textControllerKeisokuTime =
      TextEditingController();

  final TextEditingController _textControllerUpperValue =
      TextEditingController();
  final TextEditingController _textControllerLowValue = TextEditingController();
  final TextEditingController _textControllerShinpakuValue =
      TextEditingController();

  TextField createTextFieldSearchStartDate() {
    return (TextField(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          counterText: '',
        ),
        maxLines: 1,
        enabled: true,
        maxLength: 8,
        obscureText: false,
        keyboardType: TextInputType.number,
        inputFormatters: [FilteringTextInputFormatter.digitsOnly],
        controller: _textControllerSearchStartDate));
  }

  TextField createTextFieldSearchEndDate() {
    return (TextField(
        decoration: const InputDecoration(
          border: OutlineInputBorder(),
          counterText: '',
        ),
        maxLines: 1,
        enabled: true,
        maxLength: 8,
        obscureText: false,
        keyboardType: TextInputType.number,
        inputFormatters: [FilteringTextInputFormatter.digitsOnly],
        controller: _textControllerSearchEndDate));
  }

  TextField createTextFieldKeisokuDate() {
    return (TextField(
      decoration: const InputDecoration(
        border: OutlineInputBorder(),
        counterText: '',
      ),
      maxLines: 1,
      enabled: true,
      maxLength: 8,
      obscureText: false,
      keyboardType: TextInputType.number,
      inputFormatters: [FilteringTextInputFormatter.digitsOnly],
      controller: _textControllerKeisokuDate,
    ));
  }

  TextField createTextFieldKeisokuTime() {
    return (TextField(
      decoration: const InputDecoration(
        border: OutlineInputBorder(),
        counterText: '',
      ),
      maxLines: 1,
      enabled: true,
      maxLength: 4,
      obscureText: false,
      keyboardType: TextInputType.number,
      inputFormatters: [FilteringTextInputFormatter.digitsOnly],
      controller: _textControllerKeisokuTime,
    ));
  }

  TextField createTextFieldUpperValue() {
    return (TextField(
      decoration: const InputDecoration(
        border: OutlineInputBorder(),
        counterText: '',
      ),
      maxLines: 1,
      enabled: true,
      maxLength: 3,
      obscureText: false,
      keyboardType: TextInputType.number,
      inputFormatters: [FilteringTextInputFormatter.digitsOnly],
      controller: _textControllerUpperValue,
    ));
  }

  TextField createTextFieldLowValue() {
    return (TextField(
      decoration: const InputDecoration(
        border: OutlineInputBorder(),
        counterText: '',
      ),
      maxLines: 1,
      enabled: true,
      maxLength: 3,
      obscureText: false,
      keyboardType: TextInputType.number,
      inputFormatters: [FilteringTextInputFormatter.digitsOnly],
      controller: _textControllerLowValue,
    ));
  }

  // 心拍数
  TextField createTextFieldShinpakuValue() {
    return (TextField(
      decoration: const InputDecoration(
        border: OutlineInputBorder(),
        counterText: '',
      ),
      maxLines: 1,
      enabled: true,
      maxLength: 3,
      obscureText: false,
      keyboardType: TextInputType.number,
      inputFormatters: [FilteringTextInputFormatter.digitsOnly],
      controller: _textControllerShinpakuValue,
    ));
  }
}

// ダイアログ表示クラス
class DialogUtils {
  DialogUtils._();

  /// 入力した文字列を返すダイアログを表示する
  static Future<String?> showDataUpdateDialog(
    BuildContext context,
    KetsuatsuNisshiBean ketsuatsuNisshiBean,
  ) async {
    return showDialog<String>(
      context: context,
      builder: (context) {
        return DataUpdateDialog(ketsuatsuNisshiBean: ketsuatsuNisshiBean);
      },
    );
  }

  static Future<String?> showDataDeleteDialog(
    BuildContext context,
    KetsuatsuNisshiBean ketsuatsuNisshiBean,
  ) async {
    return showDialog<String>(
      context: context,
      builder: (context) {
        return DataDeleteDialog(ketsuatsuNisshiBean: ketsuatsuNisshiBean);
      },
    );
  }
}

// 更新ダイアログ
class DataUpdateDialog extends StatefulWidget {
  const DataUpdateDialog({Key? key, required this.ketsuatsuNisshiBean})
      : super(key: key);
  final KetsuatsuNisshiBean ketsuatsuNisshiBean;

  @override
  State<DataUpdateDialog> createState() => _DataUpdateDialogState();
}

class _DataUpdateDialogState extends State<DataUpdateDialog> {
  final controller1 = TextEditingController();
  final controller2 = TextEditingController();
  final controller3 = TextEditingController();
  final controller4 = TextEditingController();
  final controller5 = TextEditingController();
  final focusNode = FocusNode();

  @override
  void dispose() {
    controller1.dispose();
    focusNode.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    // TextFormFieldに初期値を代入する
    controller1.text = widget.ketsuatsuNisshiBean.keisokuDate ?? '';
    controller2.text = widget.ketsuatsuNisshiBean.keisokuTime ?? '';
    controller3.text = (widget.ketsuatsuNisshiBean.upperValue ?? 0).toString();
    controller4.text = (widget.ketsuatsuNisshiBean.lowValue ?? '0').toString();
    controller5.text =
        (widget.ketsuatsuNisshiBean.shinpakuValue ?? '0').toString();
  }

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('データ更新'),
      shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(10.0))),
      backgroundColor: Colors.yellow,
      content: ConstrainedBox(
        constraints: const BoxConstraints(
          maxHeight: 240.0,
        ),
        child: Column(children: <Widget>[
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("測定日")),
            Expanded(
              flex: 6,
              child: TextFormField(
                  autofocus: true,
                  // ダイアログが開いたときに自動でフォーカスを当てる
                  focusNode: focusNode,
                  controller: controller1,
                  keyboardType: TextInputType.number,
                  maxLength: 8,
                  decoration: const InputDecoration(
                    counterText: '',
                  ),
                  inputFormatters: [FilteringTextInputFormatter.digitsOnly]),
            ),
          ]),
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("測定時刻")),
            Expanded(
              flex: 6,
              child: TextFormField(
                  controller: controller2,
                  keyboardType: TextInputType.number,
                  maxLength: 4,
                  decoration: const InputDecoration(
                    counterText: '',
                  ),
                  inputFormatters: [FilteringTextInputFormatter.digitsOnly]),
            ),
          ]),
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("血圧(上)")),
            Expanded(
              flex: 6,
              child: TextFormField(
                  controller: controller3,
                  keyboardType: TextInputType.number,
                  maxLength: 3,
                  decoration: const InputDecoration(
                    counterText: '',
                  ),
                  inputFormatters: [FilteringTextInputFormatter.digitsOnly]),
            ),
          ]),
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("血圧(下)")),
            Expanded(
              flex: 6,
              child: TextFormField(
                  controller: controller4,
                  keyboardType: TextInputType.number,
                  maxLength: 3,
                  decoration: const InputDecoration(
                    counterText: '',
                  ),
                  inputFormatters: [FilteringTextInputFormatter.digitsOnly]),
            ),
          ]),
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("脈拍数")),
            Expanded(
              flex: 6,
              child: TextFormField(
                  controller: controller5,
                  keyboardType: TextInputType.number,
                  maxLength: 3,
                  decoration: const InputDecoration(
                    counterText: '',
                  ),
                  inputFormatters: [FilteringTextInputFormatter.digitsOnly]),
            ),
          ]),
        ]),
      ),
      actions: [
        ElevatedButton(
          style: ElevatedButton.styleFrom(
              backgroundColor: Colors.blue,
              elevation: 10,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(30.0),
              )),
          onPressed: () {
            KetsuatsuNisshiDao dao = KetsuatsuNisshiDao();
            widget.ketsuatsuNisshiBean.keisokuDate = controller1.text;
            widget.ketsuatsuNisshiBean.keisokuTime = controller2.text;
            widget.ketsuatsuNisshiBean.upperValue = int.parse(controller3.text);
            widget.ketsuatsuNisshiBean.lowValue = int.parse(controller4.text);
            widget.ketsuatsuNisshiBean.shinpakuValue =
                int.parse(controller5.text);
            dao.update(widget.ketsuatsuNisshiBean);
            Navigator.of(context).pop('OK');
          },
          child: const Text('更新'),
        ),
        ElevatedButton(
          style: ElevatedButton.styleFrom(
              backgroundColor: Colors.blue,
              elevation: 10,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(30.0),
              )),
          onPressed: () {
            Navigator.of(context).pop('cancel');
          },
          child: const Text('キャンセル'),
        )
      ],
    );
  }
}

// 削除ダイアログ
class DataDeleteDialog extends StatefulWidget {
  const DataDeleteDialog({Key? key, required this.ketsuatsuNisshiBean})
      : super(key: key);
  final KetsuatsuNisshiBean ketsuatsuNisshiBean;

  @override
  State<DataDeleteDialog> createState() => _DataDeleteDialogState();
}

class _DataDeleteDialogState extends State<DataDeleteDialog> {
  @override
  void dispose() {
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('データ削除'),
      shape: const RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(10.0))),
      backgroundColor: Colors.yellow,
      content: ConstrainedBox(
        constraints: const BoxConstraints(
          maxHeight: 120.0,
        ),
        child: Column(children: <Widget>[
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("測定日")),
            Expanded(
                flex: 6,
                child: Text(widget.ketsuatsuNisshiBean.keisokuDate ?? '')),
          ]),
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("測定時刻")),
            Expanded(
                flex: 6,
                child: Text(widget.ketsuatsuNisshiBean.keisokuTime ?? '')),
          ]),
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("血圧(上)")),
            Expanded(
                flex: 6,
                child: Text(widget.ketsuatsuNisshiBean.upperValue.toString())),
          ]),
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("血圧(下)")),
            Expanded(
                flex: 6,
                child: Text(widget.ketsuatsuNisshiBean.lowValue.toString())),
          ]),
          Row(children: <Widget>[
            const Expanded(flex: 4, child: Text("脈拍数")),
            Expanded(
                flex: 6,
                child:
                    Text(widget.ketsuatsuNisshiBean.shinpakuValue.toString())),
          ]),
        ]),
      ),
      actions: [
        ElevatedButton(
          style: ElevatedButton.styleFrom(
              backgroundColor: Colors.blue,
              elevation: 10,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(30.0),
              )),
          onPressed: () {
            KetsuatsuNisshiDao dao = KetsuatsuNisshiDao();
            dao.delete(widget.ketsuatsuNisshiBean.seqNo!);
            Navigator.of(context).pop('OK');
          },
          child: const Text('削除'),
        ),
        ElevatedButton(
          style: ElevatedButton.styleFrom(
              backgroundColor: Colors.blue,
              elevation: 10,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(30.0),
              )),
          onPressed: () {
            Navigator.of(context).pop('cancel');
          },
          child: const Text('キャンセル'),
        )
      ],
    );
  }
}

// データベースアクセス関係クラス
class DbDataSource {
  DbDataSource._privateConstructor();

  static final DbDataSource instance = DbDataSource._privateConstructor();

  static Database? _database;

  Future<Database?> get database async {
    if (_database != null) return _database;
    _database = await _initDatabase();
    return _database;
  }

  Future<Database> _initDatabase() async {
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentsDirectory.path, 'KetsuatsuNisshi3.db');
    return await openDatabase(
      path,
      version: 1,
      onCreate: _onCreate,
    );
  }

  Future _onCreate(Database db, int version) async {
    await db.execute('''
          CREATE TABLE KetsuatsuNisshi (
            SeqNo         INTEGER    PRIMARY KEY AUTOINCREMENT,
            KeisokuKubun  INTEGER    NOT NULL,
            KeisokuDate   VARCHAR(8) NOT NULL,
            KeisokuTime   VARCHAR(4) NOT NULL,
            UpperValue    INTEGER    NOT NULL,
            LowValue      INTEGER    NOT NULL,
            ShinpakuValue INTEGER    NOT NULL,
            Bikou         VARCHAR(64)
          )
          ''');
  }
}

class KetsuatsuNisshiBean {
  int? seqNo;
  int? keisokuKubun;
  String? keisokuDate;
  String? keisokuTime;
  int? upperValue;
  int? lowValue;
  int? shinpakuValue;
  String? bikou;

  KetsuatsuNisshiBean(
      this.seqNo,
      this.keisokuKubun,
      this.keisokuDate,
      this.keisokuTime,
      this.upperValue,
      this.lowValue,
      this.shinpakuValue,
      this.bikou);

  KetsuatsuNisshiBean.fromMap(Map<String, dynamic> map) {
    seqNo = map['SeqNo'];
    keisokuKubun = map['KeisokuKubun'];
    keisokuDate = map['KeisokuDate'];
    keisokuTime = map['KeisokuTime'];
    upperValue = map['UpperValue'];
    lowValue = map['LowValue'];
    shinpakuValue = map['ShinpakuValue'];
    bikou = map['Bikou'];
  }

  Map<String, dynamic> toMap() {
    return {
      'SeqNo': seqNo,
      'KeisokuKubun': keisokuKubun,
      'KeisokuDate': keisokuDate,
      'KeisokuTime': keisokuTime,
      'UpperValue': upperValue,
      'LowValue': lowValue,
      'ShinpakuValue': shinpakuValue,
      'Bikou': bikou
    };
  }
}

class KetsuatsuNisshiDao {
  Future<int> insert(KetsuatsuNisshiBean ketsuatsuNisshiBean) async {
    Database? db = await DbDataSource.instance.database;
    Map<String, dynamic> map = ketsuatsuNisshiBean.toMap();
    map.remove('SeqNo');
    return await db!.insert('KetsuatsuNisshi', map);
  }

  Future<int> update(KetsuatsuNisshiBean ketsuatsuNisshiBean) async {
    Database? db = await DbDataSource.instance.database;
    int seqNo = ketsuatsuNisshiBean.toMap()['SeqNo'];
    return await db!.update('KetsuatsuNisshi', ketsuatsuNisshiBean.toMap(),
        where: 'SeqNo = ?', whereArgs: [seqNo]);
  }

  Future<int> delete(int seqNo) async {
    Database? db = await DbDataSource.instance.database;
    return await db!
        .delete('KetsuatsuNisshi', where: 'SeqNo = ?', whereArgs: [seqNo]);
  }

  Future<List<Map<String, dynamic>>> search(
      String startKeisokuDate, String endKeisokuDate) async {
    Database? db = await DbDataSource.instance.database;
    return await db!.query('KetsuatsuNisshi',
        where: 'KeisokuDate >= ? and KeisokuDate <= ?',
        whereArgs: [startKeisokuDate, endKeisokuDate],
        orderBy:
            'KeisokuDate ASC, KeisokuKubun ASC, KeisokuTime ASC, SeqNo ASC');
  }
}

[main.dart