You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题原因

  1. 类型不匹配:Operation类中_amount是double类型,但SQLite数据库中NUM类型会根据插入值自动存储为int或double。当插入整数金额时,数据库存储为int,读取时直接赋值给double变量就会触发类型转换错误。
  2. 构造函数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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 11:55:36