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

Flutter代码报Expected an identifier错误,请求排查修复

Flutter Todo应用错误修复:Expected an identifier.dart(missing_identifier)

错误定位

报错的核心原因是TodoListScreen的build方法中,ListView.builder的itemBuilder参数未正确实现。原代码仅写了itemBuilder: (),不符合Dart语法要求——itemBuilder必须接收带BuildContext和索引int的回调函数,且需返回对应的列表项组件。同时原代码缺失了触发添加功能的输入框与按钮,导致添加逻辑无法被调用。

解决方案

替换错误的itemBuilder部分,补充完整的回调逻辑;同时添加底部输入栏与添加按钮,完善交互功能。此外还增加了空值判断与错误提示,提升代码鲁棒性和用户体验。

完整修复后的代码

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() => runApp(TodoApp());

class TodoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Todo App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: TodoListScreen(),
    );
  }
}

class Todo {
  final int id;
  final String title;
  bool isDone;

  Todo({
    required this.id,
    required this.title,
    this.isDone = false,
  });

  factory Todo.fromJson(Map<String, dynamic> json) {
    return Todo(
      id: json['id'],
      title: json['title'],
      isDone: json['isDone'] ?? false, // 增加空值判断,避免API返回null导致错误
    );
  }
}

class TodoListScreen extends StatefulWidget {
  @override
  _TodoListScreenState createState() => _TodoListScreenState();
}

class _TodoListScreenState extends State<TodoListScreen> {
  final TextEditingController _todoTitleController = TextEditingController();
  final List<Todo> _todos = [];

  @override
  void initState() {
    super.initState();
    _fetchTodos();
  }

  void _fetchTodos() async {
    final response = await http.get(Uri.parse('https://todoapp-api.apps.k8s.gu.se/todos'));
    if (response.statusCode == 200) {
      final jsonList = jsonDecode(response.body) as List<dynamic>;
      final todos = jsonList.map((json) => Todo.fromJson(json)).toList();
      setState(() {
        _todos.clear();
        _todos.addAll(todos);
      });
    } else {
      // 错误提示
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('加载待办列表失败')),
      );
    }
  }

  void _addTodo() async {
    final newTodoTitle = _todoTitleController.text.trim();
    if (newTodoTitle.isNotEmpty) {
      final response = await http.post(
        Uri.parse('https://todoapp-api.apps.k8s.gu.se/todos'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({'title': newTodoTitle, 'isDone': false}),
      );
      if (response.statusCode == 201) {
        final newTodo = Todo.fromJson(jsonDecode(response.body));
        setState(() {
          _todos.add(newTodo);
        });
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('添加待办项失败')),
        );
      }
      _todoTitleController.clear();
    }
  }

  void _toggleTodoStatus(Todo todo) async {
    final response = await http.patch(
      Uri.parse('https://todoapp-api.apps.k8s.gu.se/todos/${todo.id}'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({'isDone': !todo.isDone}),
    );
    if (response.statusCode == 200) {
      setState(() {
        todo.isDone = !todo.isDone;
      });
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('更新待办状态失败')),
      );
    }
  }

  void _deleteTodo(Todo todo) async {
    final response = await http.delete(Uri.parse('https://todoapp-api.apps.k8s.gu.se/todos/${todo.id}'));
    if (response.statusCode == 204) {
      setState(() {
        _todos.remove(todo);
      });
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('删除待办项失败')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Todo List'),
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _todoTitleController,
                    decoration: InputDecoration(
                      hintText: '输入新的待办项',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                SizedBox(width: 8),
                ElevatedButton(
                  onPressed: _addTodo,
                  child: Text('添加'),
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              itemCount: _todos.length,
              itemBuilder: (context, index) {
                final todo = _todos[index];
                return ListTile(
                  title: Text(
                    todo.title,
                    style: TextStyle(
                      decoration: todo.isDone ? TextDecoration.lineThrough : null,
                    ),
                  ),
                  leading: Checkbox(
                    value: todo.isDone,
                    onChanged: (_) => _toggleTodoStatus(todo),
                  ),
                  trailing: IconButton(
                    icon: Icon(Icons.delete, color: Colors.red),
                    onPressed: () => _deleteTodo(todo),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

额外优化说明

  • 在Todo.fromJson中增加json['isDone'] ?? false,避免API返回null时触发类型错误
  • 给所有网络请求添加了错误提示SnackBar,提升用户体验
  • 补充了输入栏和添加按钮,实现完整的待办项添加流程

内容的提问来源于stack exchange,提问作者eddin99

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 01:22:06