I'm creating a dependent dropdownbuttons using StreamBuilder
. Until now, I created three dropdownbuttons with the information showing correctly. But I get a error when I want to change the value of a higher level dropdownbutton once the process of the three dropdownbuttons showing the info in a hierachical way is done, saying this:
type Null isn't a subtype of Map<dynamic,dynamic>
This error is showed in the third StreamBuilder
, specifically in this line:
final data = Map<String, dynamic>.from(
(snapshot.data!.snapshot.value as Map));
Here's the code:
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
class NewCauseMenu extends StatefulWidget {
String? area;
NewCauseMenu(this.area, {super.key});
@override
State<NewCauseMenu> createState() => _NewCauseMenuState();
}
class _NewCauseMenuState extends State<NewCauseMenu> {
final db = FirebaseDatabase.instance;
String? dropdownValue;
String? problem;
String? causa1;
String? causa2;
String? causa3;
String? causa4;
var setDefaultProblem = true,
setDefaultCauseOne = true,
setDefaultCauseTwo = true;
@override
Widget build(BuildContext context) {
final myRef = db.ref();
return WillPopScope(
onWillPop: () {
return _onWillPopScope();
},
child: SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('Menú Nueva Causa ${widget.area}'),
),
body: Center(
child: Column(
children: [
const SizedBox(
height: 25,
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const SizedBox(
width: 25,
),
StreamBuilder(
stream: myRef.child(widget.area.toString()).onValue,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
final data = Map<String, dynamic>.from(
(snapshot.data)!.snapshot.value as Map);
if (setDefaultProblem) {
problem = data.keys.first.toString();
}
return DropdownButton(
value: problem,
icon: const Icon(Icons.arrow_downward),
isExpanded: false,
elevation: 16,
underline: Container(
height: 2,
color: Colors.blueAccent,
),
items: data.keys.map(((e) {
return DropdownMenuItem(
value: e.toString(),
child: Text(e.toString()));
})).toList(),
onChanged: (value) {
setState(() {
problem = value!;
setDefaultProblem = false;
setDefaultCauseOne = true;
});
},
);
}),
const SizedBox(
width: 25,
),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue),
child: const Text('Editar'),
),
const SizedBox(
width: 25,
),
],
),
),
const SizedBox(
height: 25,
),
problem != null
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const SizedBox(
width: 25,
),
StreamBuilder(
stream: myRef
.child(widget.area.toString())
.child(problem.toString())
.onValue,
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == null) {
return const Text(
'Aquí aparecerá las causas del problema seleccionado.');
}
final data = Map<String, dynamic>.from(
(snapshot.data!.snapshot.value as Map));
if (setDefaultCauseOne) {
causa1 = data.keys.first.toString();
}
return DropdownButton(
value: causa1,
icon: const Icon(Icons.arrow_downward),
isExpanded: false,
elevation: 16,
underline: Container(
height: 2,
color: Colors.blueAccent,
),
items: data.keys.map(((e) {
return DropdownMenuItem(
value: e.toString(),
child: Text(e.toString()));
})).toList(),
onChanged: (value) {
setState(() {
causa1 = value!;
setDefaultCauseOne = false;
setDefaultCauseTwo = true;
});
},
);
},
),
const SizedBox(
width: 25,
),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue),
child: const Text('Editar'),
),
const SizedBox(
width: 25,
),
],
),
)
: const Text(
'Aquí aparecerá las causas del problema seleccionado.'),
const SizedBox(
height: 25,
),
causa1 != null || problem != null
? Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const SizedBox(
width: 25,
),
StreamBuilder(
stream: myRef
.child(widget.area.toString())
.child(problem.toString())
.child(causa1.toString())
.onValue,
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == null) {
return const Text(
'Aquí aparecerá las subcausas del problema seleccionado.');
}
final data = Map<String, dynamic>.from(
(snapshot.data!.snapshot.value as Map));
if (setDefaultCauseTwo) {
causa2 = data.keys.first.toString();
}
return Flexible(
fit: FlexFit.loose,
child: DropdownButton(
value: causa2,
icon: const Icon(Icons.arrow_downward),
isExpanded: true,
elevation: 16,
itemHeight: null,
underline: Container(
height: 2,
color: Colors.blueAccent,
),
items: data.keys.map(((e) {
return DropdownMenuItem(
value: e.toString(),
child: Text(
e.toString(),
));
})).toList(),
onChanged: (value) {
setState(() {
causa2 = value!;
setDefaultCauseTwo = false;
});
},
),
);
}),
const SizedBox(
width: 25,
),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue),
child: const Text('Editar'),
),
const SizedBox(
width: 25,
)
],
)
: const Text(
'Aquí aparecerá las subcausas del problema seleccionado.'),
],
),
),
)),
);
}
Future<bool> _onWillPopScope() async {
Navigator.of(context).pop();
return false;
}
}
I don't know why I get this error if the values of the others StreamBuilders
are not Null. Help me, please.
CodePudding user response:
Problem:
From the error message, you're casting a null
value, snapshot.data!.snapshot.value
, to a Map
.
The value
property of a Firebase Realtime Database DataSnapshot is nullable.
Solution:
Add a null
check for snapshot.data!.snapshot.value
where you check for null
data.
Change:
if (!snapshot.hasData || snapshot.data == null) {
return const Text('Aquí aparecerá las causas del problema seleccionado.');
}
final data = Map<String, dynamic>.from(snapshot.data!.snapshot.value as Map));
to:
if (!snapshot.hasData || snapshot.data == null || snapshot.data!.snapshot.value == null) {
return const Text('Aquí aparecerá las causas del problema seleccionado.');
}
final data = Map<String, dynamic>.from(snapshot.data!.snapshot.value as Map));