質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

解決済

1回答

425閲覧

BottomNavigationBarをデータベースから非同期処理を用いて取得した情報で構築したい

Auxo

総合スコア34

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2022/09/20 17:28

Flutter(Flutter 3.3.2/Dart 2.18.1)にてBottomNavigationBarを使ったページの作成をしています。

BottomNavigationBarのアイテムリスト(LIST<BottomNavigationBarItem>)をデータベースから取得し表示したいのですが、データ取得が非同期処理の為、先にWidgetの生成が始まりその時点ではアイテムリストが空となり「_AssertionError ('package:flutter/src/material/bottom_navigation_bar.dart': Failed assertion: line 197 pos 15: 'items.length >= 2': is not true.)」といったエラーメッセージが表示されます。

エラーの意味する事は理解でき、データベースからの情報の取得が非同期処理の為、その処理完了後に「Widget build」が実施されれば良いとは思うのですが、どうやって解決すれば良いのかわかりません。

どのようにすれば「reloadTabs()」により非同期処理の実行完了後に取得した情報で「Widget build」を実行できるのでしょうか。

該当のソースコード

dart

1import 'dart:io'; 2import 'package:flutter/material.dart'; 3import 'package:sqflite/sqflite.dart'; 4import 'package:path/path.dart'; 5import 'package:path_provider/path_provider.dart'; 6 7void main() { 8 9 runApp( 10 MaterialApp( 11 debugShowCheckedModeBanner: false, 12 initialRoute: '/', 13 routes: { 14 '/': (context) => const MyApp(), 15 }, 16 ), 17 ); 18 19} 20 21class MyApp extends StatefulWidget { 22 23 const MyApp({Key? key}) : super(key: key); 24 25 26 State<MyApp> createState() => MyAppState(); 27 28} 29 30class MyAppState extends State<MyApp> { 31 32 int screenIndex = 0; 33 34 final List<Widget> pages = [ 35 const FirstPage(), 36 const SecondPage() 37 ]; 38 39 40 Widget build(BuildContext context) { 41 42 return Scaffold( 43 body: pages[screenIndex], 44 bottomNavigationBar: BottomNavigationBarPage( 45 changeTab: (int index) { 46 changeTab(index); 47 } 48 ) 49 ); 50 51 } 52 53 void changeTab(int tabIndex) { 54 55 setState(() { 56 screenIndex = tabIndex; 57 }); 58 59 } 60 61} 62 63class FirstPage extends StatefulWidget { 64 65 const FirstPage({Key? key}) : super(key: key); 66 67 68 FirstPageState createState() => FirstPageState(); 69 70} 71 72class FirstPageState extends State<FirstPage> { 73 74 75 Widget build(BuildContext context) { 76 77 return Scaffold( 78 79 body: Center( 80 child: Column( 81 mainAxisAlignment: MainAxisAlignment.center, 82 children: const <Widget>[ 83 Text( 84 '1ST Page', 85 ), 86 ], 87 ), 88 ), 89 90 ); 91 92 } 93 94} 95 96class SecondPage extends StatefulWidget { 97 98 const SecondPage({Key? key}) : super(key: key); 99 100 101 SecondPageState createState() => SecondPageState(); 102 103} 104 105class SecondPageState extends State<SecondPage> { 106 107 108 Widget build(BuildContext context) { 109 110 return Scaffold( 111 112 body: Center( 113 child: Column( 114 mainAxisAlignment: MainAxisAlignment.center, 115 children: const <Widget>[ 116 Text( 117 '2ND Page', 118 ), 119 ], 120 ), 121 ), 122 123 ); 124 125 } 126 127} 128 129class BottomNavigationBarPage extends StatefulWidget { 130 131 const BottomNavigationBarPage({Key? key, required this.changeTab}) : super(key: key); 132 133 final Function(int) changeTab; 134 135 136 BottomNavigationBarPageState createState() => BottomNavigationBarPageState(); 137 138} 139 140class BottomNavigationBarPageState extends State<BottomNavigationBarPage> { 141 142 int tabIndex = 0; 143 144 List<BottomNavigationBarItem> bottomItems = []; 145 146 147 void initState() { 148 149 super.initState(); 150 151 reloadTabs(); 152 153 } 154 155 156 Widget build(BuildContext context) { 157 158 return BottomNavigationBar( 159 items: bottomItems, 160 currentIndex: tabIndex, 161 onTap: (index) { 162 163 setState(() { 164 tabIndex = index; 165 }); 166 167 widget.changeTab(index); 168 169 }, 170 ); 171 172 } 173 174 void reloadTabs() async { 175 176 final items = await DatabaseHelper.getTabs(); 177 178 List<BottomNavigationBarItem> tabItems = []; 179 180 for (var item in items) { 181 182 tabItems.add( 183 BottomNavigationBarItem( 184 icon: const Icon(Icons.square), 185 label: item["name"], 186 ) 187 ); 188 189 } 190 191 setState(() { 192 bottomItems = tabItems; 193 }); 194 195 } 196 197} 198 199class DatabaseHelper { 200 201 static Future<List<Map<String, dynamic>>> getTabs() async { 202 203 final db = await DatabaseHelper.db(); 204 205 final rows = await db.rawQuery(""" 206 SELECT 207 tabs.id, 208 tabs.name 209 FROM 210 tabs 211 """ 212 ); 213 214 return rows; 215 216 } 217 218 static Future<void> _onUpgrade(Database db, int oldVersion, int newVersion) async { 219 220 await db.transaction((txn) async { 221 222 if(oldVersion == 0){ 223 224 await txn.execute(""" 225 CREATE TABLE "tabs" ( 226 "id" INTEGER, 227 "name" TEXT NOT NULL, 228 PRIMARY KEY("id" AUTOINCREMENT) 229 ); 230 """); 231 232 await txn.execute(""" 233 INSERT INTO tabs(name) 234 VALUES("1ST") 235 """); 236 237 await txn.execute(""" 238 INSERT INTO tabs(name) 239 VALUES("2ND") 240 """); 241 242 } 243 244 }); 245 246 } 247 248 static Future<Database> db() async { 249 250 final dbFilePath = await getDbPath(); 251 252 debugPrint(dbFilePath); 253 254 return openDatabase( 255 dbFilePath, 256 version: 3, 257 onUpgrade: _onUpgrade, 258 ); 259 260 } 261 262 static Future<String> getDbPath() async { 263 264 var dbFilePath = ''; 265 266 if (Platform.isAndroid) { 267 dbFilePath = await getDatabasesPath(); 268 } else if (Platform.isIOS) { 269 final dbDirectory = await getLibraryDirectory(); 270 dbFilePath = dbDirectory.path; 271 } 272 273 final path = join(dbFilePath, 'app.db'); 274 275 return path; 276 277 } 278 279}

なお、動作の為に以下のパッケージを導入しております。

pubspec.yaml

1sqflite: ^2.0.3+1 2path: ^1.8.2 3path_provider: ^2.0.11

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こういうasyncな関数の結果をもってWidgetを構築するのはFutureBuilderを使うのがFlutterの標準の作法です。
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

ただ今回提示されたコードだとFutureBuilderに対応するには変更箇所がちょっと多そうなので、使わない形のコードを提示しておきます。(たぶん動くと思うけど)
変更前

dart

1 return BottomNavigationBar(

変更後

dart

1 return bottomItems.isEmpty? const CircularProgressIndicator():BottomNavigationBar(

まだDBからのロードが完了しておらずbottomItemsの中身が空だったらCircularProgressIndicatorを代わりに表示するという感じ。
またDBからのロード時間が極端に短い場合にはCircularProgressIndicatorではなくからのコンテナなどを配置してからにしておくというのもありかもしれない。

投稿2022/09/20 21:53

ta.fu

総合スコア1667

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Auxo

2022/09/21 01:12

ご回答ありがとう御座います。 確かに中身が空だったら・・・と処理すればいいと・・・。 言われてみればその通りです。 こんな単純な、と思いつつも全く思い至りませんでした。 また「FutureBuilder」は使っていなかったでの、少し勉強したいと思います。 今回はご指摘のように空の場合は代替の何かを返す方法で無事対応できました。 ありがとう御座いました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問