错误状态:使用 firstWhere 函数时没有元素

Bad State: no element when using firstWhere function

当我尝试比较从路由中获取的参数与列表的值时,我遇到了上述错误。

MealDetailScreen 小部件

import 'package:flutter/material.dart';

import '../dummy_data.dart';

class MealDetailScreen extends StatelessWidget {
  static const detailScreenRouteName = '/meal-detail';

  @override
  Widget build(BuildContext context) {
    final mealId = ModalRoute.of(context)!.settings.arguments;
    final selectedMeal = DUMMY_MEALS.firstWhere((meal) => meal.id == mealId);
    return Scaffold(
      appBar: AppBar(title: Text('$mealId')),
      body: Container(
        child: Column(
          children: [
            Container(
              height: 300,
              width: double.infinity,
              child: Image.network(
                selectedMeal.imageUrl,
                fit: BoxFit.cover,
              ),
            )
          ],
        ),
      ),
    );
  }
}

如果我尝试将可选参数 'orElse' 添加到 firstWhere 函数,我仍然会收到错误消息:这次是 return 类型 'Null' 不是't a 'Meal',根据闭包上下文的要求。

这是我用来比较 id 的列表。

const DUMMY_MEALS = [
  Meal(
    isVegetarian: false,
    isLactoseFree: false,
    isVegan: false,
    id: 'm1',
    categories: [
      'c1',
      'c2',
    ],
    title: 'Spaghetti with Tomato Sauce',
    affordability: Affordability.Affordable,
    complexity: Complexity.Simple,
    imageUrl: '...',
    duration: 20,
    ingredients: ['4 Tomatoes', '...'],
    steps: ['Cut the tomatoes and the onion into small pieces.', '...'],
    isGlutenFree: false,
  ),
];

这就是我将 id 作为参数传递的方式

void selectMeal(BuildContext context) {
    Navigator.of(context)
        .pushNamed(MealDetailScreen.detailScreenRouteName, arguments: {id});
  }

餐模

import 'package:flutter/foundation.dart';

enum Complexity { Simple, Challenging, Hard }

enum Affordability { Affordable, Pricey, Luxurious }

class Meal {
  final String id;
  final List<String> categories;
  final String title;
  final String imageUrl;
  final List<String> ingredients;
  final List<String> steps;
  final int duration;
  final Complexity complexity;
  final Affordability affordability;
  final bool isGlutenFree;
  final bool isLactoseFree;
  final bool isVegan;
  final bool isVegetarian;

  const Meal(
      {required this.id,
      required this.categories,
      required this.title,
      required this.imageUrl,
      required this.ingredients,
      required this.steps,
      required this.duration,
      required this.complexity,
      required this.affordability,
      required this.isGlutenFree,
      required this.isLactoseFree,
      required this.isVegan,
      required this.isVegetarian});
}

在超过 arguments: {id} 时,你正在超过 _HashSet<String>。但是对于单个值 id string 就足够了,否则使用 map.

在这种情况下传递参数将是

  Navigator.of(context)
        .pushNamed(MealDetailScreen.detailScreenRouteName, arguments: id);

在迭代 DUMMY_MEALS 列表时,我们可能会得到 DUMMY_MEALS 列表中未包含的 id。在这种情况下,您可以在 orElse 状态下创建并传递 emptyMeal 或仅使用 try catch 来处理异常。


    Meal? selectedMeal;

    try {
      final selectedMeal = DUMMY_MEALS.firstWhere((meal) => meal.id == mealId);
    } catch (e) {
      print(e.toString());
    }

虽然 selectedMeal 可以为空,但我们可以检查它是否包含膳食。

 return Scaffold(
      appBar: AppBar(title: Text('$mealId')),
      body: selectedMeal == null
          ? Text("Cound not find data")
          : Container(
              child: Column(
                children: [
                  Text(selectedMeal.title),
                ],
              ),
            ),
    );

答案已经给出。但我想提一下,如果您使用的是 pushNamed 方法,建议您使用 onGenerateRoute 管理传递参数。所以你不需要 nullcheck 参数或上下文。

ModalRoute.of(context)?.settings?.arguments? 
MaterialApp(
  onGenerateRoute: (settings) {
    if (settings.name == PassArgumentsScreen.routeName) {
      final args = settings.arguments as ScreenArguments;
      return MaterialPageRoute(
        builder: (context) {
          return PassArgumentsScreen(
            title: args.title,
            message: args.message,
          );
        },
      );
    }
    assert(false, 'Need to implement ${settings.name}');
    return null;
  },
)

References: Pass arguments to a named route