Home > OS >  Unhandled Exception: FormatException: Unexpected end of input (at character 1) in flutter (in shared
Unhandled Exception: FormatException: Unexpected end of input (at character 1) in flutter (in shared

Time:05-09

I'm making a task book, on a flutter, in this project I will use a lot of packages that I have not worked with before (this is not my first question on this project). My goal is to create tasks to add to the status (finished, not finished), and save them locally on the device. I wrote code (with some packages to help me figure it out) that does it all. But the problem arose when downloading questions from the local repository when starting the application. I have an error not to decode the data (in the repository I save a list of our tasks and status). Maybe someone faced such a problem I will be grateful for your help)

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

My erorr

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): 

CodePudding user response:

Change null check value from '' to '[]'.

You set jsonData with type List. In case prefs.getString('todoLists') is null, jsonDecode will take '' as parameter, but '' is not a valid json and cause the FormatException.

// final List<dynamic> jsonData = jsonDecode(prefs.getString('todoLists') ?? '');
-> final List<dynamic> jsonData = jsonDecode(prefs.getString('todoLists') ?? '[]');

Example: decode strings. enter image description here

Updated: copy code in the file and paster to dartpad. since I don't have much time, I have omitted the things that are not so important, please add it after the main thread is up and running normally to run. check the dartpad console, you can see everytime you make an update to data, writeTodos was called, place your code in there (dartpad cant use sharedPref library)

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

  • Related