Flutter加载本地SQLite数据时出现int转double类型转换异常求助
Flutter应用加载本地数据时类型转换错误排查
问题场景
开发Flutter应用时,从本地SQLite存储加载Operation数据时,触发类型转换错误:int无法转换为double。
错误日志
E/flutter ( 6175): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'int' is not a subtype of type 'double' E/flutter ( 6175): #0 new Operation.fromMap (package:flutter_example/model/operation.dart:52:5) E/flutter ( 6175): #1 LocalDatabase.getOperationList (package:flutter_example/provider/local_db.dart:60:39) E/flutter ( 6175): <asynchronous suspension> E/flutter ( 6175): #2 new MonitorBloc.<anonymous closure> (package:flutter_example/bloc/monitor_bloc.dart:22:30) E/flutter ( 6175): <asynchronous suspension> E/flutter ( 6175): #3 Bloc.on.<anonymous closure>.handleEvent (package:bloc/src/bloc.dart:226:13) E/flutter ( 6175): <asynchronous suspension>
相关代码
Operation类
class Operation { String _description = ""; bool _isEntry = true; String _date = ""; double _amount = 0.0; String get description => _description; bool get isEntry => _isEntry; String get date => _date; double get amount => _amount; set description(String description) { if(description.isNotEmpty) { _description = description; } } set date(String date) { if(date.isNotEmpty) { _date = date; } } set amount(double amount) { _amount = amount; } set isEntry(bool isEntry) { _isEntry = isEntry; } var now = DateTime.now(); var formatter = DateFormat('yyyy-MM-dd'); Operation() { _amount = 0.0; _description = ""; _isEntry = false; _date = formatter.format(now); } Operation.withData({amount = 0.0, description = "", isEntry = false, date = ""}) { _amount = _amount; _description = description; _isEntry = isEntry; _date = date; } Operation.fromMap(map) { _amount = map["amount"]; _description = map["description"]; _isEntry = map["isEntry"] == 0; _date = map["date"]; } toMap() { var map = <String, dynamic>{}; map["amount"] = _amount; map["description"] = _description; map["isEntry"] = _isEntry ? 0 : 1; map["date"] = _date; return map; } }
LocalDatabase类
import 'dart:async'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:sqflite/sqflite.dart'; import '../model/operation.dart'; import '../model/operations.dart'; class LocalDatabase { static LocalDatabase helper = LocalDatabase._createInstance(); LocalDatabase._createInstance(); static Database? _database; Future<Database> get database async { _database ??= await initializeDatabase(); return _database!; } String operationTable = "operation_table"; String colId = "id"; String colAmount = "amount"; String colDescription = "description"; String colIsEntry = "isEntry"; String colDate = "date"; _createDb(Database db, int newVersion) { db.execute(""" CREATE TABLE $operationTable ( $colId INTEGER PRIMARY KEY AUTOINCREMENT, $colAmount NUM, $colDescription TEXT, $colDate TEXT, $colIsEntry INT ); """); } Future<Database> initializeDatabase() async { Directory directory = await getApplicationDocumentsDirectory(); String path = "${directory.path}operations_2.db"; return openDatabase(path, version: 1, onCreate: _createDb); } Future<int> insertOperation(Operation operation) async { Database? db = await database; int result = await db.insert(operationTable, operation.toMap()); notify(result.toString(), operation); return result; } Future<OperationsCollection> getOperationList() async { Database db = await database; List<Map<String, Object?>> operationMapList = await db.rawQuery("SELECT * FROM $operationTable;"); OperationsCollection operationsCollection = OperationsCollection(); for (int i = 0; i < operationMapList.length; i++) { Operation operation = Operation.fromMap(operationMapList[i]); operationsCollection.insertOperationOfId(operationMapList[i][colId].toString(), operation); } return operationsCollection; } // Stream notify(String operationId, Operation? operation) async { _controller?.sink.add([operationId, operation]); } Stream get stream { _controller ??= StreamController.broadcast(); return _controller!.stream; } dispose() { if (_controller != null) { if (!_controller!.hasListener) { _controller!.close(); _controller = null; } } } static StreamController? _controller; }
问题原因
- 类型不匹配:
Operation类中_amount是double类型,但SQLite数据库中NUM类型会根据插入值自动存储为int或double。当插入整数金额时,数据库存储为int,读取时直接赋值给double变量就会触发类型转换错误。 - 构造函数bug:
Operation.withData构造函数中_amount = _amount;是无效赋值,永远会将_amount设为初始值0.0。
解决方案
1. 修复fromMap类型转换
修改Operation.fromMap方法,将读取到的数值统一转换为double:
Operation.fromMap(map) { // 将num类型(int/double)转为double,同时处理null情况 _amount = (map["amount"] as num?)?.toDouble() ?? 0.0; _description = map["description"] ?? ""; _isEntry = map["isEntry"] == 0; _date = map["date"] ?? ""; }
2. 优化数据库表结构
将amount字段指定为REAL类型,强制存储为浮点型,避免自动类型转换:
_createDb(Database db, int newVersion) { db.execute(""" CREATE TABLE $operationTable ( $colId INTEGER PRIMARY KEY AUTOINCREMENT, $colAmount REAL, $colDescription TEXT, $colDate TEXT, $colIsEntry INT ); """); }
注意:修改表结构后需要卸载重装应用,或者添加数据库版本迁移逻辑,否则旧表不会更新。
3. 修复withData构造函数bug
修正无效赋值问题,同时可为日期参数添加默认值:
Operation.withData({double amount = 0.0, String description = "", bool isEntry = false, String date = ""}) { _amount = amount; _description = description; _isEntry = isEntry; // 若日期为空则使用当前日期 _date = date.isNotEmpty ? date : formatter.format(now); }
内容的提问来源于stack exchange,提问作者Robson Fischer
相关产品推荐
相关产品推荐

