未处理的异常:FormatException:颤动中的输入意外结束(在字符 1 处)(在 shared_preferences 中)
Unhandled Exception: FormatException: Unexpected end of input (at character 1) in flutter (in shared_preferences)
我正在制作一个任务书,在这个项目中,我将使用很多我以前没有使用过的包(这不是我对这个项目的第一个问题)。我的目标是创建任务以添加到状态(已完成、未完成),并将它们保存在本地设备上。我写了代码(用一些包来帮助我弄明白)来完成这一切。但是在启动应用程序时从本地存储库下载问题时出现了问题。我有一个不解码数据的错误(在存储库中我保存了我们的任务和状态列表)。也许有人遇到过这样的问题我会很感激你的帮助)
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:testjob/jsonTodo.dart';
void main() {
runApp(MaterialApp(
home: App(),
));
}
class _strikeThrough extends StatelessWidget {
final String todoText;
final bool todoCheck;
_strikeThrough(this.todoText, this.todoCheck) : super();
Widget _widget() {
if (todoCheck) {
return Text(
todoText,
style: TextStyle(
fontSize: 22.0,
),
);
} else {
return Text(
todoText,
style: TextStyle(fontSize: 22.0),
);
}
}
@override
Widget build(BuildContext context) {
return _widget();
}
}
class App extends StatefulWidget {
@override
AppState createState() {
return AppState();
}
}
final ValueNotifier<ThemeMode> _notifier = ValueNotifier(ThemeMode.light);
class AppState extends State<App> {
bool valText = true;
var IconsType = Icons.wb_sunny;
late Color ColorType = Colors.black;
var textController = TextEditingController();
var popUpTextController = TextEditingController();
List<TodoInfo> WidgetList = [];
@override
void dispose() {
textController.dispose();
popUpTextController.dispose();
super.dispose();
}
@override
void initState() {
getSP();
super.initState();
}
Future<void> addToSP(List<List<TodoInfo>> tList) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString('todoLists', jsonEncode(tList));
}
void getSP() async {
final prefs = await SharedPreferences.getInstance();
final List<dynamic> jsonData =
jsonDecode(prefs.getString('todoLists') ?? '');
if (jsonData.isNotEmpty) {
for (var data in jsonData) {
final d = TodoInfo.fromJson(data);
WidgetList.add(d);
}
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: _notifier,
builder: (_, mode, __) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: mode, // Decides which theme to show, light or dark.
home: Scaffold(
appBar: AppBar(
title: Text("Список задач"),
actions: <Widget>[
IconButton(
icon: Icon(IconsType, color: ColorType),
onPressed: () => {
if (_notifier.value == ThemeMode.light)
{
_notifier.value = ThemeMode.dark,
IconsType = Icons.dark_mode,
ColorType = Colors.white,
}
else
{
_notifier.value = ThemeMode.light,
IconsType = Icons.wb_sunny,
ColorType = Colors.black,
}
})
],
//backgroundColor: Colors.orange[500],
iconTheme: IconThemeData(color: Colors.white),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"Tasks",
style: TextStyle(
fontSize: 70.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
IconButton(
color: Colors.black,
iconSize: 70,
constraints: const BoxConstraints(),
padding: EdgeInsets.fromLTRB(30.0, 10.0, 30, 10.0),
icon: const Icon(Icons.add_outlined),
onPressed: () {
if (textController.text
.replaceAll(" ", "")
.isNotEmpty) {
WidgetList.insert(0, TodoInfo(todoText:textController.text.replaceAll(" ", ""), todoCheck: false ));
addToSP;
setState(() {
valText = true;
textController.clear();
});
} else {
setState(() {
valText = false;
});
}
},
)
],
),
),
Container(
width: MediaQuery.of(context).size.height * 0.45,
child: TextField(
style: TextStyle(
fontSize: 22.0,
//color: Theme.of(context).accentColor,
),
controller: textController,
cursorWidth: 5.0,
autocorrect: true,
autofocus: true,
//onSubmitted: ,
),
),
Align(
child: (valText == false)
? Align(
child: Text(("Задача пустая"),
style: TextStyle(
fontSize: 25.0, color: Colors.red)),
alignment: Alignment.center)
: Align(
child: Text(
(""),
),
alignment: Alignment.center)),
Expanded(
child: ReorderableListView(
children: <Widget>[
for (final widget in WidgetList)
GestureDetector(
key: Key(widget.todoText),
child: Dismissible(
key: Key(widget.todoText),
child: CheckboxListTile(
controlAffinity:
ListTileControlAffinity.leading,
//key: ValueKey("Checkboxtile $widget"),
value: widget.todoCheck,
title: _strikeThrough(
widget.todoText, widget.todoCheck),
onChanged: (checkValue) {
//_strikethrough toggle
setState(() {
if (!checkValue!) {
widget.todoCheck = false;
} else {
widget.todoCheck = true;
}
});
},
),
background: Container(
child: Icon(Icons.delete),
alignment: Alignment.centerRight,
color: Colors.redAccent,
),
direction: DismissDirection.endToStart,
movementDuration:
const Duration(milliseconds: 200),
onDismissed: (dismissDirection) {
//Delete Todo
WidgetList.remove(widget);
},
),
)
],
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
var replaceWiget = WidgetList.removeAt(oldIndex);
WidgetList.insert(newIndex, replaceWiget);
});
},
),
)
],
),
));
});
}
}
class TodoInfo {
String todoText;
bool todoCheck;
TodoInfo({
required this.todoText,
required this.todoCheck,
});
factory TodoInfo.fromJson(Map<String, dynamic> json) {
return TodoInfo(todoText: json["todoText"], todoCheck: json["todoCheck"]);
}
factory TodoInfo.fromMap(Map<String, dynamic> map) =>
TodoInfo(
todoText: map["todoText"] ?? '',
todoCheck: map["todoCheck"] ?? '',
);
Map<String, dynamic> toJson() {
return {"todoText": todoText, "todoCheck": todoCheck};
}
@override
String toString() => '{todoText: $todoText, todoCheck: $todoCheck}';
}
我的错误
E/flutter (30831): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: FormatException: Unexpected end of input (at character 1)
E/flutter (30831):
E/flutter (30831): ^
E/flutter (30831):
E/flutter (30831): #0 _ChunkedJsonParser.fail (dart:convert-patch/convert_patch.dart:1404:5)
E/flutter (30831): #1 _ChunkedJsonParser.close (dart:convert-patch/convert_patch.dart:522:7)
E/flutter (30831): #2 _parseJson (dart:convert-patch/convert_patch.dart:41:10)
E/flutter (30831): #3 JsonDecoder.convert (dart:convert/json.dart:506:36)
E/flutter (30831): #4 JsonCodec.decode (dart:convert/json.dart:157:41)
E/flutter (30831): #5 jsonDecode (dart:convert/json.dart:96:10)
E/flutter (30831): #6 AppState.getSP (package:testjob/main.dart:85:5)
E/flutter (30831): <asynchronous suspension>
E/flutter (30831):
将空检查值从 ''
更改为 '[]'
。
您将 jsonData
设置为类型 List。如果 prefs.getString('todoLists')
为 null,jsonDecode
将采用 ''
作为参数,但 ''
为 无效json 并导致 FormatException.
// final List<dynamic> jsonData = jsonDecode(prefs.getString('todoLists') ?? '');
-> final List<dynamic> jsonData = jsonDecode(prefs.getString('todoLists') ?? '[]');
示例:解码字符串。
更新:复制文件中的代码并粘贴到 dartpad。由于时间不多,省略了一些不太重要的东西,请在主线程启动后添加,正常运行ning到运行。检查 dartpad 控制台,你可以看到每次更新数据时,都会调用 writeTodos,将你的代码放在那里(dartpad 不能使用 sharedPref 库)
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: App()));
class App extends StatefulWidget {
@override
AppState createState() => AppState();
}
class AppState extends State<App> {
final textController = TextEditingController();
final formKey = GlobalKey<FormState>();
List<TodoInfo> data = [];
@override
void initState() {
readTodos();
super.initState();
}
@override
void dispose() {
textController.dispose();
super.dispose();
}
void submitAddTodo() {
if (formKey.currentState?.validate() ?? false) {
final todoString = textController.text;
if (todoString.isNotEmpty) {
var todo = TodoInfo(todoText: todoString.trim(), todoCheck: false);
addTodo(todo);
textController.clear();
}
}
}
void removeTodoAt(int index) {
setState(() {
data = [...data]..removeAt(index);
writeTodos();
});
}
void updateTodoAt(int index, TodoInfo todo) {
setState(() {
data[index] = todo;
writeTodos();
});
}
void addTodo(TodoInfo todo) {
setState(() {
data = [todo, ...data];
writeTodos();
});
}
Future<void> writeTodos() async {
// write data to shared
print('writeTodos');
}
void readTodos() async {
print('readTodos');
// read data from your shared insteads hard code `prefsData`
final prefsData = [
TodoInfo(todoText: 'todo 1', todoCheck: false),
TodoInfo(todoText: 'todo 2', todoCheck: false),
];
setState(() => data = prefsData);
}
@override
Widget build(BuildContext context) {
return Scaffold(
//...
body: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"Tasks",
style: TextStyle(
fontSize: 70.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
IconButton(
color: Colors.black,
iconSize: 70,
constraints: const BoxConstraints(),
padding: EdgeInsets.fromLTRB(30.0, 10.0, 30, 10.0),
icon: const Icon(Icons.add_outlined),
onPressed: submitAddTodo,
)
],
),
),
Container(
width: MediaQuery.of(context).size.height * 0.45,
child: TextFormField(
style: TextStyle(fontSize: 22.0),
controller: textController,
autofocus: true,
validator: (value) {
if (value == null || value.isEmpty) return 'Список задач';
},
),
),
Expanded(child: _rTodos())
],
),
),
);
}
Widget _rTodos() {
var wgs = <Widget>[];
for (int i = 0; i < data.length; i++) {
var todo = data[i];
wgs.add(GestureDetector(
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
value: todo.todoCheck,
title: Text(todo.todoText, style: TextStyle(fontSize: 22.0)),
onChanged: (checkValue) =>
updateTodoAt(i, todo..todoCheck = checkValue ?? false),
),
));
}
return ListView(
children: wgs
);
}
}
class TodoInfo {
String todoText;
bool todoCheck;
TodoInfo({required this.todoText, required this.todoCheck});
}
我正在制作一个任务书,在这个项目中,我将使用很多我以前没有使用过的包(这不是我对这个项目的第一个问题)。我的目标是创建任务以添加到状态(已完成、未完成),并将它们保存在本地设备上。我写了代码(用一些包来帮助我弄明白)来完成这一切。但是在启动应用程序时从本地存储库下载问题时出现了问题。我有一个不解码数据的错误(在存储库中我保存了我们的任务和状态列表)。也许有人遇到过这样的问题我会很感激你的帮助)
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:testjob/jsonTodo.dart';
void main() {
runApp(MaterialApp(
home: App(),
));
}
class _strikeThrough extends StatelessWidget {
final String todoText;
final bool todoCheck;
_strikeThrough(this.todoText, this.todoCheck) : super();
Widget _widget() {
if (todoCheck) {
return Text(
todoText,
style: TextStyle(
fontSize: 22.0,
),
);
} else {
return Text(
todoText,
style: TextStyle(fontSize: 22.0),
);
}
}
@override
Widget build(BuildContext context) {
return _widget();
}
}
class App extends StatefulWidget {
@override
AppState createState() {
return AppState();
}
}
final ValueNotifier<ThemeMode> _notifier = ValueNotifier(ThemeMode.light);
class AppState extends State<App> {
bool valText = true;
var IconsType = Icons.wb_sunny;
late Color ColorType = Colors.black;
var textController = TextEditingController();
var popUpTextController = TextEditingController();
List<TodoInfo> WidgetList = [];
@override
void dispose() {
textController.dispose();
popUpTextController.dispose();
super.dispose();
}
@override
void initState() {
getSP();
super.initState();
}
Future<void> addToSP(List<List<TodoInfo>> tList) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString('todoLists', jsonEncode(tList));
}
void getSP() async {
final prefs = await SharedPreferences.getInstance();
final List<dynamic> jsonData =
jsonDecode(prefs.getString('todoLists') ?? '');
if (jsonData.isNotEmpty) {
for (var data in jsonData) {
final d = TodoInfo.fromJson(data);
WidgetList.add(d);
}
setState(() {});
}
}
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: _notifier,
builder: (_, mode, __) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: mode, // Decides which theme to show, light or dark.
home: Scaffold(
appBar: AppBar(
title: Text("Список задач"),
actions: <Widget>[
IconButton(
icon: Icon(IconsType, color: ColorType),
onPressed: () => {
if (_notifier.value == ThemeMode.light)
{
_notifier.value = ThemeMode.dark,
IconsType = Icons.dark_mode,
ColorType = Colors.white,
}
else
{
_notifier.value = ThemeMode.light,
IconsType = Icons.wb_sunny,
ColorType = Colors.black,
}
})
],
//backgroundColor: Colors.orange[500],
iconTheme: IconThemeData(color: Colors.white),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"Tasks",
style: TextStyle(
fontSize: 70.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
IconButton(
color: Colors.black,
iconSize: 70,
constraints: const BoxConstraints(),
padding: EdgeInsets.fromLTRB(30.0, 10.0, 30, 10.0),
icon: const Icon(Icons.add_outlined),
onPressed: () {
if (textController.text
.replaceAll(" ", "")
.isNotEmpty) {
WidgetList.insert(0, TodoInfo(todoText:textController.text.replaceAll(" ", ""), todoCheck: false ));
addToSP;
setState(() {
valText = true;
textController.clear();
});
} else {
setState(() {
valText = false;
});
}
},
)
],
),
),
Container(
width: MediaQuery.of(context).size.height * 0.45,
child: TextField(
style: TextStyle(
fontSize: 22.0,
//color: Theme.of(context).accentColor,
),
controller: textController,
cursorWidth: 5.0,
autocorrect: true,
autofocus: true,
//onSubmitted: ,
),
),
Align(
child: (valText == false)
? Align(
child: Text(("Задача пустая"),
style: TextStyle(
fontSize: 25.0, color: Colors.red)),
alignment: Alignment.center)
: Align(
child: Text(
(""),
),
alignment: Alignment.center)),
Expanded(
child: ReorderableListView(
children: <Widget>[
for (final widget in WidgetList)
GestureDetector(
key: Key(widget.todoText),
child: Dismissible(
key: Key(widget.todoText),
child: CheckboxListTile(
controlAffinity:
ListTileControlAffinity.leading,
//key: ValueKey("Checkboxtile $widget"),
value: widget.todoCheck,
title: _strikeThrough(
widget.todoText, widget.todoCheck),
onChanged: (checkValue) {
//_strikethrough toggle
setState(() {
if (!checkValue!) {
widget.todoCheck = false;
} else {
widget.todoCheck = true;
}
});
},
),
background: Container(
child: Icon(Icons.delete),
alignment: Alignment.centerRight,
color: Colors.redAccent,
),
direction: DismissDirection.endToStart,
movementDuration:
const Duration(milliseconds: 200),
onDismissed: (dismissDirection) {
//Delete Todo
WidgetList.remove(widget);
},
),
)
],
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
var replaceWiget = WidgetList.removeAt(oldIndex);
WidgetList.insert(newIndex, replaceWiget);
});
},
),
)
],
),
));
});
}
}
class TodoInfo {
String todoText;
bool todoCheck;
TodoInfo({
required this.todoText,
required this.todoCheck,
});
factory TodoInfo.fromJson(Map<String, dynamic> json) {
return TodoInfo(todoText: json["todoText"], todoCheck: json["todoCheck"]);
}
factory TodoInfo.fromMap(Map<String, dynamic> map) =>
TodoInfo(
todoText: map["todoText"] ?? '',
todoCheck: map["todoCheck"] ?? '',
);
Map<String, dynamic> toJson() {
return {"todoText": todoText, "todoCheck": todoCheck};
}
@override
String toString() => '{todoText: $todoText, todoCheck: $todoCheck}';
}
我的错误
E/flutter (30831): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: FormatException: Unexpected end of input (at character 1)
E/flutter (30831):
E/flutter (30831): ^
E/flutter (30831):
E/flutter (30831): #0 _ChunkedJsonParser.fail (dart:convert-patch/convert_patch.dart:1404:5)
E/flutter (30831): #1 _ChunkedJsonParser.close (dart:convert-patch/convert_patch.dart:522:7)
E/flutter (30831): #2 _parseJson (dart:convert-patch/convert_patch.dart:41:10)
E/flutter (30831): #3 JsonDecoder.convert (dart:convert/json.dart:506:36)
E/flutter (30831): #4 JsonCodec.decode (dart:convert/json.dart:157:41)
E/flutter (30831): #5 jsonDecode (dart:convert/json.dart:96:10)
E/flutter (30831): #6 AppState.getSP (package:testjob/main.dart:85:5)
E/flutter (30831): <asynchronous suspension>
E/flutter (30831):
将空检查值从 ''
更改为 '[]'
。
您将 jsonData
设置为类型 List。如果 prefs.getString('todoLists')
为 null,jsonDecode
将采用 ''
作为参数,但 ''
为 无效json 并导致 FormatException.
// final List<dynamic> jsonData = jsonDecode(prefs.getString('todoLists') ?? '');
-> final List<dynamic> jsonData = jsonDecode(prefs.getString('todoLists') ?? '[]');
示例:解码字符串。
更新:复制文件中的代码并粘贴到 dartpad。由于时间不多,省略了一些不太重要的东西,请在主线程启动后添加,正常运行ning到运行。检查 dartpad 控制台,你可以看到每次更新数据时,都会调用 writeTodos,将你的代码放在那里(dartpad 不能使用 sharedPref 库)
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: App()));
class App extends StatefulWidget {
@override
AppState createState() => AppState();
}
class AppState extends State<App> {
final textController = TextEditingController();
final formKey = GlobalKey<FormState>();
List<TodoInfo> data = [];
@override
void initState() {
readTodos();
super.initState();
}
@override
void dispose() {
textController.dispose();
super.dispose();
}
void submitAddTodo() {
if (formKey.currentState?.validate() ?? false) {
final todoString = textController.text;
if (todoString.isNotEmpty) {
var todo = TodoInfo(todoText: todoString.trim(), todoCheck: false);
addTodo(todo);
textController.clear();
}
}
}
void removeTodoAt(int index) {
setState(() {
data = [...data]..removeAt(index);
writeTodos();
});
}
void updateTodoAt(int index, TodoInfo todo) {
setState(() {
data[index] = todo;
writeTodos();
});
}
void addTodo(TodoInfo todo) {
setState(() {
data = [todo, ...data];
writeTodos();
});
}
Future<void> writeTodos() async {
// write data to shared
print('writeTodos');
}
void readTodos() async {
print('readTodos');
// read data from your shared insteads hard code `prefsData`
final prefsData = [
TodoInfo(todoText: 'todo 1', todoCheck: false),
TodoInfo(todoText: 'todo 2', todoCheck: false),
];
setState(() => data = prefsData);
}
@override
Widget build(BuildContext context) {
return Scaffold(
//...
body: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"Tasks",
style: TextStyle(
fontSize: 70.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
IconButton(
color: Colors.black,
iconSize: 70,
constraints: const BoxConstraints(),
padding: EdgeInsets.fromLTRB(30.0, 10.0, 30, 10.0),
icon: const Icon(Icons.add_outlined),
onPressed: submitAddTodo,
)
],
),
),
Container(
width: MediaQuery.of(context).size.height * 0.45,
child: TextFormField(
style: TextStyle(fontSize: 22.0),
controller: textController,
autofocus: true,
validator: (value) {
if (value == null || value.isEmpty) return 'Список задач';
},
),
),
Expanded(child: _rTodos())
],
),
),
);
}
Widget _rTodos() {
var wgs = <Widget>[];
for (int i = 0; i < data.length; i++) {
var todo = data[i];
wgs.add(GestureDetector(
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
value: todo.todoCheck,
title: Text(todo.todoText, style: TextStyle(fontSize: 22.0)),
onChanged: (checkValue) =>
updateTodoAt(i, todo..todoCheck = checkValue ?? false),
),
));
}
return ListView(
children: wgs
);
}
}
class TodoInfo {
String todoText;
bool todoCheck;
TodoInfo({required this.todoText, required this.todoCheck});
}