繁体   English   中英

Flutter 资产数据库错误:在 null 上调用了 getter 'length'。 接收方:null 尝试调用:长度

[英]Flutter asset database error: The getter 'length' was called on null. Receiver: null Tried calling: length

我使用 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

这是我的 model 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));
            },
          );
        });
  }
}

如何在我的资产数据库中列出项目并在应用程序中查看?

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
        });
  }

它很容易,你必须在使用 snapshot.data.lenght 之前检查是否有来自未来的数据,因为如果 snapshot.data 为空(操作尚未完成),那么 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
             }
        });
  }
}

并且您可以添加检查将来发生的任何执行

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM