我使用 sqflite 将现有的 database.db
文件添加到我的项目中。没有遇到错误,一切正常,但是... Flutter 调试控制台说:
Restarted application in 772ms.
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building FutureBuilder<List<Countries>>(dirty, state: _FutureBuilderState<List<Countries>>#d0317):
The getter 'length' was called on null.
Receiver: null
Tried calling: length
The relevant error-causing widget was
FutureBuilder<List<Countries>>
When the exception was thrown, this was the stack
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 _HomeScreen.buildBody.<anonymous closure>
#2 _FutureBuilderState.build
#3 StatefulElement.build
#4 ComponentElement.performRebuild
...
════════════════════════════════════════════════════════════════════════════════
I/flutter (14052): Opening existing database
这是我的模型 Country.dart
:
class Countries {
int countryId;
String countryName;
String countryImageURL;
//Constructor
Countries({this.countryId, this.countryName, this.countryImageURL});
// Extract a Product Object from a Map Oject
Countries.fromMap(Map<String, dynamic> map) {
countryId = map['country_id'];
countryName = map['country_name'];
countryImageURL = map['image'];
}
Map<String, dynamic> toMap() {
var map = <String, dynamic>{
'country_name': countryName,
'image': countryImageURL
};
return map;
}
}
这是我的 database_helper.dart
文件:
import 'dart:async';
import 'dart:io';
import 'package:city_travel_guide/model/Country.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'dart:typed_data';
import 'package:flutter/services.dart';
class DbHelper {
static Database _db;
Future<Database> get db async {
if (_db != null) {
return _db;
} else {
_db = await initDb();
return _db;
}
}
initDb() async {
var dbFolder = await getDatabasesPath();
String path = join(dbFolder, 'app.db');
var exists = await databaseExists(path);
if (!exists) {
// Should happen only the first time you launch your application
print("Creating new copy from asset");
// Make sure the parent directory exists
try {
await Directory(dirname(path)).create(recursive: true);
} catch (_) {}
// Copy from asset
ByteData data = await rootBundle.load(join("assets", "example.db"));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
// Write and flush the bytes written
await File(path).writeAsBytes(bytes, flush: true);
} else {
print("Opening existing database");
}
// open the database
return await openDatabase(path);
}
Future<List<Countries>> getCountries() async {
var dbClient = await db;
var result = await dbClient.query('Country', orderBy: 'countryId');
return result.map((data) => Countries.fromMap(data)).toList();
}
这是我的 main.dart
文件:
import 'package:city_travel_guide/data/database_helper.dart';
import 'package:city_travel_guide/model/Country.dart';
import 'package:flutter/material.dart';
import 'widgets/maindrawer.dart';
import 'pages/search.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'City Travel Guide',
theme: ThemeData.dark(),
debugShowCheckedModeBanner: false,
home: MyHome());
}
}
class MyHome extends StatefulWidget {
@override
_HomeScreen createState() => _HomeScreen();
}
class _HomeScreen extends State<MyHome> {
List<Countries> countries;
final dbHelper = DbHelper();
@override
void initState() {
dbHelper.initDb();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'City Travel Guide',
style: Theme.of(context).primaryTextTheme.headline6,
),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SearchScreen()),
);
}),
IconButton(icon: const Icon(Icons.more_vert), onPressed: () {}),
],
),
drawer: Drawer(child: MainDrawer()),
body: buildBody(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {},
));
}
buildBody() {
return FutureBuilder<List<Countries>>(
future: dbHelper.getCountries(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(title: Text(snapshot.data[index].countryName));
},
);
});
}
}
如何在我的资产数据库中列出项目并在应用程序中查看?
答案 0 :(得分:0)
FutureBuilder 是一个异步请求。在构建列表之前,请始终检查快照是否包含数据。 做:
buildBody() {
return FutureBuilder<List<Countries>>(
future: dbHelper.getCountries(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data.length > 0) // This ensures that you have at least one or more countries available.
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(title: Text(snapshot.data[index].countryName));
},
);
else if (snapshot.hasData && snapshot.data.length == 0)
return Center(child:Text("There are no countries available"));
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).primaryColor),
)); // This would display a loading animation before your data is ready
});
}
答案 1 :(得分:0)
在使用snapshot.data.lenght之前,您必须检查是否有来自未来的数据很容易,因为如果snapshot.data为null(操作尚未完成),则lenght调用null所以你必须这样做
正确的代码
buildBody() {
return FutureBuilder<List<Countries>>(
future: dbHelper.getCountries(),
builder: (context, snapshot) {
if(snapshot.hasdata&&snapshot.data.runtimetype==List){
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(title: Text(snapshot.data[index].countryName));
},
);
}else{
return Proggresindicator()//or any loading widgets
}
});
}
}
并且您可以添加对未来发生的任何执行的检查