Dart编码解码异常:String无法转为Map<String, dynamic>求助
问题分析与解决方案
问题描述
- 调用
encodeSelectedEvents后得到输出:"{\"2023-04-19 00:00:00.000Z\":[{\"title\":\"sdaads\"}]}" - 执行解码操作时触发异常:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'String' is not a subtype of type 'Map<String, dynamic>' - 核心障碍:无法将String类型转换为Map<String, dynamic>,使用生成的转换函数仍未解决问题。
核心原因
代码中存在重复JSON编码的问题:
encodeSelectedEvents函数内部已经通过json.encode(encodedEvents)生成了JSON字符串- 存储到SharedPreferences时,又额外调用了
json.encode(encodeSelectedEvents(...)),导致JSON字符串被再次编码 - 最终存储的内容是一个被转义的字符串(而非原始JSON对象结构),解码时
json.decode()返回的是String类型,无法直接转换为Map,触发类型不匹配异常。
修复步骤
- 移除重复编码:直接存储
encodeSelectedEvents的返回值,无需二次调用json.encode - (可选)统一日期格式:使用
DateTime.toIso8601String()生成标准日期字符串,避免解析时的时区或格式问题
修正后的完整代码
import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; class Event{ final String title; Event({required this.title}); Map<String, dynamic> toJson() => { 'title': title, }; factory Event.fromJson(Map<String, dynamic> json) { return Event( title: json['title'], ); } } class CalendarPage extends StatefulWidget { const CalendarPage({Key? key}) : super(key: key); @override State<CalendarPage> createState() => _CalendarPageState(); } class _CalendarPageState extends State<CalendarPage> { String encodeSelectedEvents(Map<DateTime, List<Event>> selectedEvents) { Map<String, List<Map<String, dynamic>>> encodedEvents = {}; selectedEvents.forEach((key, value) { // 可选:使用标准ISO日期格式,提升解析可靠性 encodedEvents[key.toIso8601String()] = value.map((event) => event.toJson()).toList(); }); return json.encode(encodedEvents); } Map<DateTime, List<Event>> decodeSelectedEvents(String jsonString) { Map<String, dynamic> decodedEvents = json.decode(jsonString); Map<DateTime, List<Event>> selectedEvents = {}; decodedEvents.forEach((key, value) { DateTime dateTimeKey = DateTime.parse(key); selectedEvents[dateTimeKey] = List<Event>.from(value.map((event) => Event.fromJson(event))); }); return selectedEvents; } Map<DateTime, List<Event>>? selectedEvents = {}; DateTime today = DateTime.now(); void _OnDaySelected(DateTime day, DateTime focusedDay) { setState(() { today = day; }); } @override void initState() { super.initState(); initializeDateFormatting("tr_TR"); selectedEvents = {}; } List<Event> _getEventsfromDay(DateTime day) { return selectedEvents![day]?? []; } @override Widget build(BuildContext context) { return Center( child: SingleChildScrollView( child: Column( children: [ TableCalendar( locale: "tr_TR", firstDay: DateTime.utc(2023, 01, 01), lastDay: DateTime.utc(2025, 01, 01), focusedDay: today, headerStyle: HeaderStyle( formatButtonVisible: false, titleCentered: true, ), availableGestures: AvailableGestures.all, rowHeight: 35, onDaySelected: _OnDaySelected, selectedDayPredicate: (day) =>isSameDay(day,today), eventLoader: _getEventsfromDay, ), ElevatedButton(onPressed: () async{ final secim = await Navigator.push(context,MaterialPageRoute(builder: (builder) => EventAddPage(date: today.toString().split(" ")[0],))); if (secim != false) { final SharedPreferences prefs = await SharedPreferences.getInstance(); var events = prefs.getString('events'); // 首次初始化 if (events == null){ await prefs.setString("events", "{}"); selectedEvents = {}; }else { selectedEvents = decodeSelectedEvents(events); } // 更新事件列表 if (selectedEvents == null) { selectedEvents = {today: [Event(title: secim)]}; } else if (selectedEvents![today]?.isNotEmpty ?? false) { selectedEvents![today]!.add(Event(title: secim)); } else { selectedEvents![today] = [Event(title: secim)]; } setState(() {}); // 移除重复的json.encode,直接存储encodeSelectedEvents的结果 await prefs.setString("events", encodeSelectedEvents(selectedEvents!)); } }, child: Text("Seçili güne event ekle") ), ..._getEventsfromDay(today).map((Event event) => ListTile(title: Text(event.title),)), ], ), ), ); } } // 补充EventAddPage的占位实现(原代码未提供) class EventAddPage extends StatelessWidget { final String date; const EventAddPage({Key? key, required this.date}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Add Event for $date")), body: Center( child: ElevatedButton( onPressed: () => Navigator.pop(context, "Sample Event"), child: Text("Add Event"), ), ), ); } }
内容的提问来源于stack exchange,提问作者Wiro
相关产品推荐
相关产品推荐

