I want to show my readings that are coming from Esp32 to my Heart Widget that is displayed on HomeScreen.
I used the example code of flutter_blue package to check whether readings are coming to the app, but I am having difficulty understanding the code as I am new to Flutter.
My Bluetooth Connection Screen looks like this, which shows the list of nearby BLE devices
When I press Connect Button it goes to this screen showing the services, descriptors, and MTU size.
As clearly seen in the picture that the last service is getting values from ESP32, I want to show the values under Characteristic to show in Heart Rate Widget, where 96 is written. But I can't understand how I should display the value of the characteristic on this screen and not on the above screen.
These are the values that I am getting on the console as well, which are correct. I just want that when I press the connect button of ESP32, it should connect instantly and instead of going to 2nd screen it should go to the home screen and show values inside the heart rate widget
My code for Bluetooth Scanning Devices Screen is:
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'BluetoothConnectBand.dart';
import 'widgets.dart';
class FlutterBlueApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
//backgroundColor:Colors.lightBlue ,
body: StreamBuilder<BluetoothState>(
stream: FlutterBlue.instance.state,
initialData: BluetoothState.unknown,
builder: (c, snapshot) {
final state = snapshot.data;
if (state == BluetoothState.on) {
return FindDevicesScreen();
}
return BluetoothOffScreen(state: state);
}),
);
}
}
class BluetoothOffScreen extends StatelessWidget {
const BluetoothOffScreen({Key key, this.state}) : super(key: key);
final BluetoothState state;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlue,
appBar:AppBar(
backgroundColor: Colors.lightBlue,
elevation: 0,
leading:IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return BluetoothConnectBand();
},
),
);
},
),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.bluetooth_disabled,
size: 200.0,
color: Colors.white54,
),
Text(
'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
style: Theme.of(context)
.primaryTextTheme
.subhead
?.copyWith(color: Colors.white),
),
],
),
),
);
}
}
class FindDevicesScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xffE5E0A1),
elevation: 0,
centerTitle: true,
title: Text(
"Connect Band",
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontFamily: 'Montserrat',
fontWeight: FontWeight.normal,
),
),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return BluetoothConnectBand();
},
),
);
},
),
),
body: RefreshIndicator(
onRefresh: () =>
FlutterBlue.instance.startScan(timeout: Duration(seconds: 4)),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: size.height * 0.4,
width: size.width,
color: const Color(0xffE5E0A1),
child: Image.asset(
'assets/images/bluetooth.png',
)),
Container(
width: size.width,
padding: EdgeInsets.symmetric(vertical: 20),
child: Text(
"Scanning Available Devices...",
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Montserrat',
),
textAlign: TextAlign.center,
),
),
//Expanded(child:_buildListViewOfDevices()),
],
),
StreamBuilder<List<BluetoothDevice>>(
stream: Stream.periodic(Duration(seconds: 2))
.asyncMap((_) => FlutterBlue.instance.connectedDevices),
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map((d) => ListTile(
title: Text(d.name,
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontWeight: FontWeight.w600,
fontFamily: 'Montserrat',
),
),
subtitle: Text(d.id.toString(),
style: TextStyle(
fontSize: 13.0,
color: Colors.black,
fontWeight: FontWeight.w500,
fontFamily: 'Montserrat',
),
),
trailing: StreamBuilder<BluetoothDeviceState>(
stream: d.state,
initialData: BluetoothDeviceState.disconnected,
builder: (c, snapshot) {
if (snapshot.data ==
BluetoothDeviceState.connected) {
return FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28.0),
),
child: Text('Connected'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
DeviceScreen(device: d))),
);
}
return Text(snapshot.data.toString(),
style: TextStyle(
color: Colors.green,
),);
},
),
))
.toList(),
),
),
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
return DeviceScreen(device: r.device);
})),
),
)
.toList(),
),
),
],
),
),
),
floatingActionButton: StreamBuilder<bool>(
stream: FlutterBlue.instance.isScanning,
initialData: false,
builder: (c, snapshot) {
if (snapshot.data) {
return FloatingActionButton(
child: Icon(Icons.stop),
onPressed: () => FlutterBlue.instance.stopScan(),
backgroundColor: Colors.red,
);
} else {
return FloatingActionButton(
child: Icon(Icons.search),
onPressed: () => FlutterBlue.instance
.startScan(timeout: Duration(seconds: 4)),
backgroundColor: const Color(0xffE5E0A1),);
}
},
),
);
}
}
class DeviceScreen extends StatelessWidget {
const DeviceScreen({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
List<int> _getRandomBytes() {
final math = Random();
return [
math.nextInt(255),
math.nextInt(255),
math.nextInt(255),
math.nextInt(255)
];
}
List<Widget> _buildServiceTiles(List<BluetoothService> services) {
return services
.map(
(s) => ServiceTile(
service: s,
characteristicTiles: s.characteristics
.map(
(c) => CharacteristicTile(
characteristic: c,
onReadPressed: () => c.read(),
onWritePressed: () async {
await c.write(_getRandomBytes(), withoutResponse: true);
await c.read();
},
onNotificationPressed: () async {
await c.setNotifyValue(!c.isNotifying);
await c.read();
},
descriptorTiles: c.descriptors
.map(
(d) => DescriptorTile(
descriptor: d,
onReadPressed: () => d.read(),
onWritePressed: () => d.write(_getRandomBytes()),
),
)
.toList(),
),
)
.toList(),
),
)
.toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(device.name),
actions: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) {
VoidCallback onPressed;
String text;
switch (snapshot.data) {
case BluetoothDeviceState.connected:
onPressed = () => device.disconnect();
text = 'DISCONNECT';
break;
case BluetoothDeviceState.disconnected:
onPressed = () => device.connect();
text = 'CONNECT';
break;
default:
onPressed = null;
text = snapshot.data.toString().substring(21).toUpperCase();
break;
}
return FlatButton(
onPressed: onPressed,
child: Text(
text,
style: Theme.of(context)
.primaryTextTheme
.button
?.copyWith(color: Colors.white),
));
},
)
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) => ListTile(
leading: (snapshot.data == BluetoothDeviceState.connected)
? Icon(Icons.bluetooth_connected)
: Icon(Icons.bluetooth_disabled),
title: Text(
'Device is ${snapshot.data.toString().split('.')[1]}.'),
subtitle: Text('${device.id}'),
trailing: StreamBuilder<bool>(
stream: device.isDiscoveringServices,
initialData: false,
builder: (c, snapshot) => IndexedStack(
index: snapshot.data ? 1 : 0,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () => device.discoverServices(),
),
IconButton(
icon: SizedBox(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.grey),
),
width: 18.0,
height: 18.0,
),
onPressed: null,
)
],
),
),
),
),
StreamBuilder<int>(
stream: device.mtu,
initialData: 0,
builder: (c, snapshot) => ListTile(
title: Text('MTU Size'),
subtitle: Text('${snapshot.data} bytes'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () => device.requestMtu(223),
),
),
),
StreamBuilder<List<BluetoothService>>(
stream: device.services,
initialData: [],
builder: (c, snapshot) {
return Column(
children: _buildServiceTiles(snapshot.data),
);
},
),
],
),
),
);
}
}
Code for Widgets is the same as written in the code example, I have only changed main file for my UI.
Code for Heart Rate Widget:
Center(
child: Container(
height: size.height * 0.190,
width: size.width * 0.80,
padding: EdgeInsets.all(11.0),
child: Card(
color: const Color(0xffe8e5af),
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
alignment: Alignment.centerLeft,
child: const SpinKitPumpingHeart(
color: Colors.white,
)
//Image.asset(
// 'assets/images/heart_button.png',
// ),
),
Container(
padding: EdgeInsets.symmetric(vertical: 20),
alignment: Alignment.center,
width: size.width * 0.80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'96',
style: TextStyle(
fontFamily: 'SF Pro Display',
fontSize: 19,
color: const Color(0xffffffff),
fontWeight: FontWeight.w500,
height: 1.4736842105263157,
),
textHeightBehavior: TextHeightBehavior(
applyHeightToFirstAscent: false),
textAlign: TextAlign.left,
),
SizedBox(
height: 5,
),
Text(
'Heart Rate',
style: TextStyle(
fontFamily: 'SF Pro Display',
fontSize: 19,
color: Colors.white.withOpacity(0.7),
fontWeight: FontWeight.w500,
height: 1.2777777777777777,
),
textHeightBehavior: TextHeightBehavior(
applyHeightToFirstAscent: false),
textAlign: TextAlign.left,
),
],
),
)
],
),
),
),
)
One way to solve it is to use a Provider.
With one class
import 'package:flutter_blue/flutter_blue.dart';
class BlueDevice extends ChangeNotifier {
/// Internal, private state of the Device.
BluetoothDevice _device;
BluetoothService _service;
/// Device metrics
int heartRate;
...
/// Adds device to model. This is the only way to modify the device from outside.
void add(BluetoothDevice d) async {
_device = d;
await _device.connect();
_device.discoverServices();
_device.services.listen((lista) {
for (int iService = 0; iService < lista.length; iService++) {
if (lista[iService].uuid.toString().startsWith("YOUR UUID")) {
_service = lista[iService];
}
}
});
// This line tells [Model] that it should rebuild the widgets that
// depend on it.
notifyListeners();
}
void read() async {
List<List> values = [];
if (_service != null) {
for (BluetoothCharacteristic c in _service.characteristics) {
values.add(await c.read());
}
}
heartRate = values[0][0];
notifyListeners();
}
}
need to create the provider
class FlutterBlueApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
// In this app, BlueDevice is implemented as a ChangeNotifier,
// which calls for the use of ChangeNotifierProvider.
create: (context) => BlueDevice(),
child: Scaffold(...
and you may use it with a consumer
Consumer<BlueDevice>(
builder: (context, blue, child) => Text(blue.heartRate.toString(),
//'96', THIS WAS THE VALUE
style: TextStyle(
fontFamily: 'SF Pro Display',
fontSize: 19,
color: const Color(0xffffffff),
fontWeight: FontWeight.w500,
height: 1.4736842105263157,
),
Related
I want to navigate to several new pages using my bottom navigation bar in flutter.
I tried several methods to achieve my requirement. But still neither of them worked out for me. I have attached my relevant entire source code below for a better understanding.
Thank you in advance. :)
Dart Source Code :
class dashboard extends StatefulWidget {
#override
_dashboardState createState() => _dashboardState();
}
// ignore: camel_case_types
class _dashboardState extends State<dashboard> {
#override
Widget build(BuildContext context) {
final authService = Provider.of<AuthService>(context);
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 80.0, right: 250),
child: Center(
child: Container(
width: 200.0,
height: 20.0,
decoration:
BoxDecoration(borderRadius: BorderRadius.circular(15.0)),
child: (const Text(
'Hello',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold, color: Colors.black),
)),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: MaterialButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => profile()));
},
child: const Text('Test'),
),
),
Padding(
padding: EdgeInsets.only(left: 300.0, top: 10.0),
child: IconButton(
icon: new Icon(Icons.notifications),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Notifications(),
),
);
},
),
),
Padding(
padding: const EdgeInsets.only(top: 1.0),
child: Center(
child: Container(
width: 390,
height: 450,
decoration: BoxDecoration(
color: Colors.green.shade100,
borderRadius: BorderRadius.circular(10.0)),
),
),
),
],
),
),
floatingActionButton: FloatingActionButton(onPressed: () async {
await authService.signOut();
}),
// : _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.book_online),
label: 'Page 1',
),
BottomNavigationBarItem(
icon: Icon(Icons.read_more),
label: 'Page 2',
),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
label: 'Page 3',
),
],
),
);
}
}
There are many propositions, this is one :
first, you begin by adding a variable index to know where you are. then add a function to change the content of this variable.
class _dashboardState extends State< dashboard > {
int currentIndex = 1;
changeIndex(index) {
setState(() {
currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: WillPopScope(
child: (currentIndex == 1)
? HomeScreen()
: (currentIndex == 2)
? Screen2()
: (currentIndex == 3)
? Screen3()
: Settings(),
onWillPop: () async {
bool backStatus = onWillPop();
if (backStatus) {
exit(0);
}
return false;
},
),
then link it to the navbar button.
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const [
InkWell(
onTap: () => changeIndex(1),
child: BottomNavigationBarItem(
icon: Icon(Icons.book_online),
label: 'Page 1',
),
.....
],
),
This question already has answers here:
Null check operator used on a null value
(12 answers)
Closed last year.
I am new to programming and don't know how to solve this issue.
I am using the java version of 8 and using Android Studio to make apps with Flutter.
I have been making a to-do list app, and a cast error happens when
I make a new list
I update a list
I delete a list
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter ToDo List',
debugShowCheckedModeBanner: false,
theme: ThemeData(
fontFamily: "NanumSquare",
primaryColor: Color(0xFF424874), //primary color
),
home: HomeScreen()
);
}
}
this is the main.dart
the error appears on the Home Screen.
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
late Future<List<Note>> _noteList;
final DateFormat _dateFormatter = DateFormat("MMM dd, yyyy");
DatabaseHelper _databaseHelper = DatabaseHelper.instance;
void initState() {
super.initState();
_updateNoteList();
}
_updateNoteList() {
_noteList = DatabaseHelper.instance.getNoteList();
}
Widget _buildNote(Note note) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 25),
child: Column(
children: [
Container(
margin: EdgeInsets.all(5),
color: Color(0xFFDCD6F7),
child: ListTile(
title: Text(
note.title!,
style: TextStyle(
fontSize: 20,
color: Color(0xFF424874),
decoration: note.status == 0
? TextDecoration.none
: TextDecoration.lineThrough),
),
subtitle: Text(
"${_dateFormatter.format(note.date!)}-${note.priority}",
style: TextStyle(
fontSize: 15,
color: Color(0xFF424874),
decoration: note.status == 0
? TextDecoration.none
: TextDecoration.lineThrough),
),
trailing: Checkbox(
onChanged: (value) {
note.status = value! ? 1 : 0;
DatabaseHelper.instance.updateNote(note);
_updateNoteList();
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => HomeScreen())
);
},
activeColor: Color(0xFFA6B1E1),
value: note.status == 1 ? true : false,
),
onTap: () => Navigator.push(
context,
CupertinoPageRoute(
builder: (_) => AddNoteScreen(
updateNoteList: _updateNoteList(),
note: note
)
),
),
),
),
Divider(
height: 5,
color: Color(0xFFA6B1E1),
thickness: 2,
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFF4EEFF),
floatingActionButton: FloatingActionButton(
backgroundColor: Color(0xFF424874),
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (_) => AddNoteScreen(
updateNoteList: _updateNoteList(),
),
));
},
child: Icon(Icons.add),
),
body: FutureBuilder(
future: _noteList,
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final int completedNoteCount = snapshot.data!.where((Note note) => note.status == 1).toList().length;
return ListView.builder(
padding: EdgeInsets.symmetric(vertical: 80),
itemCount: int.parse(snapshot.data!.length.toString()) + 1,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Padding(
padding:
EdgeInsets.symmetric(horizontal: 40, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"My Notes",
style: TextStyle(
color: Color(0xFF424874),
fontSize: 40,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 10,
),
Text(
"$completedNoteCount of ${snapshot.data.length}",
style: TextStyle(
color: Color(0xFFA6B1E1),
fontSize: 20,
fontWeight: FontWeight.w600),
),
],
),
);
}
return _buildNote(snapshot.data![index-1]);
});
}));
}
}
The error message is as below.
======== Exception caught by gesture ===============================================================
The following _CastError was thrown while handling a gesture:
Null check operator used on a null value
When the exception was thrown, this was the stack:
#0 _AddNoteScreenState._delete (package:crud_sqlite_app/screens/add_note_screen.dart:80:26)
#1 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:989:21)
#2 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
#3 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11)
#4 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#e7613
debugOwner: GestureDetector
state: ready
won arena
finalPosition: Offset(150.8, 655.2)
finalLocalPosition: Offset(110.8, 46.2)
button: 1
sent tap down
====================================================================================================
and this is the add_note_screen.dart
class AddNoteScreen extends StatefulWidget {
final Note? note;
final Function? updateNoteList;
AddNoteScreen({this.note, this.updateNoteList});
#override
_AddNoteScreenState createState() => _AddNoteScreenState();
}
class _AddNoteScreenState extends State<AddNoteScreen> {
final _formKey = GlobalKey<FormState>();
String _title = "";
String _priority = "Low";
String btnText = "Add Note";
String titleText = "Add Note";
DateTime _date = DateTime.now();
TextEditingController _dateController = TextEditingController();
final DateFormat _dateFormatter = DateFormat("MMM dd, yyyy");
final List<String> _priorities = ["Low", "Medium", "High"];
#override
void initState(){
super.initState();
if(widget.note != null) {
_title = widget.note!.title!;
_date = widget.note!.date!;
_priority = widget.note!.priority!;
setState(() {
btnText = "Update Note";
titleText = "Update Note";
});
}
else {
setState(() {
btnText = "Add Note";
titleText = "Add Note";
});
}
_dateController.text = _dateFormatter.format(_date);
}
#override
void dispose() {
_dateController.dispose();
super.dispose();
}
_handleDatePicker() async {
final DateTime? date = await showDatePicker(
context: context,
initialDate: _date,
firstDate: DateTime(2000),
lastDate: DateTime(2100));
if (date != null && date != _date) {
setState(() {
_date = date;
});
_dateController.text = _dateFormatter.format(date);
}
}
_delete() {
DatabaseHelper.instance.deleteNote(widget.note!.id!);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_)=> HomeScreen()
)
);
widget.updateNoteList!();
}
_submit() {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
print("$_title, $_date, $_priority");
Note note = Note(
title: _title,
date: _date,
priority: _priority
);
if (widget.note == null){
note.status = 0;
DatabaseHelper.instance.insertNote(note);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_)=> HomeScreen())
);
}
else{
note.id = widget.note!.id;
note.status = widget.note!.status;
DatabaseHelper.instance.updateNote(note);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_)=> HomeScreen())
);
}
widget.updateNoteList!();
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFFF4EEFF),
body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
GestureDetector(
onTap: () => Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => HomeScreen(),
)
),
child: Icon(
Icons.arrow_back,
size: 30,
color: Color(0xFFA6B1E1),
),
),
SizedBox(
height: 20.0,
),
Text(
titleText,
style: TextStyle(
color: Color(0xFF424874),
fontSize: 40,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 10,
),
Form(
key: _formKey,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: TextFormField(
style: TextStyle(fontSize: 18),
decoration: InputDecoration(
labelText: "Title",
labelStyle: TextStyle(fontSize: 18),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10))),
validator: (input) => input!.trim().isEmpty
? "Please enter a note title"
: null,
onSaved: (input) => _title = input!,
initialValue: _title,
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: TextFormField(
readOnly: true, //hide keyboard
controller: _dateController,
style: TextStyle(fontSize: 18),
onTap: _handleDatePicker,
decoration: InputDecoration(
labelText: "Date",
labelStyle: TextStyle(fontSize: 18),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10))),
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: DropdownButtonFormField(
isDense: true,
icon: Icon(Icons.arrow_drop_down_circle),
iconSize: 22,
iconEnabledColor: Color(0xFF424874),
items: _priorities.map((String priority) {
return DropdownMenuItem(
value: priority,
child: Text(
priority,
style: TextStyle(
color: Colors.black,
fontSize: 18,
),
));
}).toList(),
style: TextStyle(fontSize: 18),
decoration: InputDecoration(
labelText: "Priority",
labelStyle: TextStyle(fontSize: 18),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)
)
),
// validator: (input) => _priority == null ? "Please",
onChanged: (value) {
setState(() {
_priority = value.toString();
});
},
value: _priority,
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 20),
height: 60.0,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30)),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Theme.of(context).primaryColor)
),
child: Text(
btnText,
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: _submit,
),
),
widget.note != null ? Container(
margin: EdgeInsets.symmetric(vertical: 20),
height: 60,
width: double.infinity,
decoration: BoxDecoration(
color: Color(0xFF424874),
borderRadius: BorderRadius.circular(30)
),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Theme.of(context).primaryColor)
),
child: Text("Delete Note",
style: TextStyle(
color: Colors.white,
fontSize: 20
),
),
onPressed: _delete,
),
): SizedBox.shrink()
],
),
)
],
),
),
),
),
);
}
}
I am sorry the code is too long.
If you can help me, I will really appreciate it.
Thank you!
This error occurs when you use a bang operator (!) on a nullable instance which wasn't initialized. This operator should only be used when you are sure that the variable cannot be null, I don't recommend using it. Check out this answer
I'm building my quiz app and my quizpage is taking solid shape but here's a problem i have. I added a Willpopscope just so that my users can't go back to the beginning when the quiz has started and it brings up a dialog box. Here's the code:
import 'dart:convert';
//import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class GetJson extends StatelessWidget {
const GetJson({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: DefaultAssetBundle.of(context).loadString("assets/python.json"),
builder: (context, snapshot) {
var mydata = jsonDecode(snapshot.data.toString());
if (mydata == null) {
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
} else {
return QuizPage();
}
},
);
}
}
class QuizPage extends StatefulWidget {
QuizPage({Key? key}) : super(key: key);
#override
_QuizPageState createState() => _QuizPageState();
}
class _QuizPageState extends State<QuizPage> {
Widget choicebutton() {
return Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),
child: MaterialButton(
onPressed: () {},
child: Text(
"option 1",
style: TextStyle(
color: Colors.white,
fontFamily: "Alike",
fontSize: 16.0,
),
maxLines: 1,
),
color: Colors.indigo,
splashColor: Colors.indigo[700],
highlightColor: Colors.indigo[700],
minWidth: 200.0,
height: 45.0,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
),
);
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]);
return WillPopScope(
onWillPop: () {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
"Quizstar",
),
content: Text("You Can't Go Back At This Stage"),
actions: [
// ignore: deprecated_member_use
FlatButton(onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"Ok",
),
)
],
)
);
},
child: Scaffold(
body: Column(
children: [
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
"This is a sample question which will be displayed?",
style: TextStyle(
fontSize: 16.0,
fontFamily: "Quando",
),
),
),
),
Expanded(
flex: 6,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
choicebutton(),
choicebutton(),
choicebutton(),
choicebutton(),
],
),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.topCenter,
child: Center(
child: Text(
"30",
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.w700,
fontFamily: "Times New Roman",
),
),
),
),
),
],
),
),
);
}
}
The error is in the showDialog() but i can't seem to figure it out.
Thanks a lot guys.
This might help
On your willpop scope function do this instead
onWillPop: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
"Quizstar",
),
content: Text("You Can't Go Back At This Stage"),
actions: [
// ignore: deprecated_member_use
FlatButton(onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"Ok",
),
)
],
)
);
return true;
),
child : your widget
Okay i figured it out
return WillPopScope(
onWillPop: () async {
print("Back Button Pressed");
final shouldPop = await showWarning(context);
return shouldPop ?? false;
},
Then i went ahead to define showWarning like this, then defined my dialog box inside it:
Future<bool?> showWarning(BuildContext context) async => showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text("Quizstar"),
content: Text("You cannot go back at this stage"),
actions: [
ElevatedButton(
onPressed: () => Navigator.pop(context, false),
child: Text("Okay"),
),
//ElevatedButton(
// onPressed: () => Navigator.pop(context, true),
// child: Text("Yes"),
//),
],
),
);
Tips: Don't forget to make the future a bool by writing inside it with it's '?'
I am retrieving data from Firestore in the form of two functions (_onpressed() and _drname()) and called both of them in FutureBuilder.
Function 1 is
Future _onPressed() async {
if (widget.brew.id == currentid.userid()) {
return await db
.collection('Messages')
.doc(widget.brew.id)
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('${documentSnapshot.data()['Message']}');
String msg = json.encode(documentSnapshot.data()['Message']);
return msg;
} else {
print('Document does not exist on the database');
}
// var a= documentSnapshot.data()['Message'];
});
} else {
return 'No Prescription from doctor yet';
}}
Function 2 is
Future _drname() async {
if (widget.brew.id == currentid.userid()) {
return await db
.collection('Drname')
.doc(widget.brew.id)
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('${documentSnapshot.data()['name']}');
String msg = json.encode(documentSnapshot.data()['name']);
return msg;
} else {
print('Document does not exist on the database');
}
// var a= documentSnapshot.data()['Message'];
});
} else {
return 'No';
}}
Calling these functions in FutureBuilder like this
Widget _messagePannel() {
return FutureBuilder(
future: Future.wait([_onPressed(),_drname()]),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
return Scaffold(
appBar: AppBar(
actions: [
Padding(
padding: const EdgeInsets.only(right: 17.0),
child: TextButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Call()),
);
},
icon: Icon(
Icons.video_call,
color: Colors.white,
size: 30.0,
),
label: Text(''),
),
)
],
title: Text(
'Prescrption',
style: TextStyle(fontFamily: 'RussoOne', fontSize: 22.0),
),
backgroundColor: Colors.green[900],
elevation: 0.0,
centerTitle: true,
),
body: Container(
decoration: image,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
color: Colors.black,
child: Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0.0, 4.0, 4.0, 4.0),
child: Wrap(
children: [
Center(
child: Text(
'${snapshot.data[0]} ',
textAlign: TextAlign.left,
style: TextStyle(
fontFamily: 'RussoOne',
color: Colors.white,
letterSpacing: 0.8,
fontSize: 18.0,
backgroundColor: Colors.black),
),
),
Center(
child: Text(
'${snapshot.data[1]} ',
textAlign: TextAlign.left,
style: TextStyle(
fontFamily: 'RussoOne',
color: Colors.white,
letterSpacing: 0.8,
fontSize: 18.0,
backgroundColor: Colors.black),
),
),
],
),
)),
),
),
),
],
),
),
),
);
});}
Getting This error from the Debug Console
The following NoSuchMethodError was thrown building FutureBuilder<List>(dirty, state: _FutureBuilderState<List>#44f46):
The method '[]' was called on null.
Receiver: null
Tried calling:
When using snapshot, the initial value for the data property is null (since Future has no result until you receive the response), so using it straight away as you do it in the code (e.g. snapshot.data[1]) should be avoided.
To cope with that, first of all you should check if the data is not null. For that, snapshot has a dedicated method snapshot.hasData, for instance:
return FutureBuilder(
future: Future.wait([_onPressed(),_drname()]),
builder: (BuildContext context, AsyncSnapshot<List> snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator(); // E.g. Show loader if there is no data yet
}
return Scaffold(...); // Return the widget you have now
},
);
I am not using any button in alert dialog, so in action how can we prevent the overflow the alert dialog, if am using gesture detector or inkwell to get ontap or onpress function or is there any other method to do it
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return UnicornAlertDialog(
title: Column(
children: <Widget>[
Container(
child: Image.asset('images/done.png'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
)
],
),
content: Text('You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 15.0)),
gradient: LinearGradient(
colors: <Color>[
Color(0xDD4a00e0),
Color(0xFF8e2de2),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
actions: <Widget>[
Container(
child: new GestureDetector(
onTap:(){
Navigator.push(context,
MaterialPageRoute(builder: (context) => ThirdRoute()));
} ,
),
),
]
);
});
}
You are getting overflow error due to GestureDetector used inside actions property of the dialog. If you just want user to tap anywhere on the alertDialog, you can wrap the AlertDialog with GestureDetector. With this, when user taps anywhere on the dialog, it will navigate them to thirdRoute. Working code below:
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return GestureDetector(
child: AlertDialog(
title:
Column(
children: <Widget>[
Container(
child: Image.asset('images/done.png'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
),
),
)
],
),
content:
Text('You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black, fontSize: 15.0)),
// gradient: LinearGradient(
// colors: <Color>[
// Color(0xDD4a00e0),
// Color(0xFF8e2de2),
// ],
// begin: Alignment.topCenter,
// end: Alignment.bottomCenter,
// ),
actions: <Widget>[]
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => NextScreen()));
}
);
});
}
Hope this answers your question.
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return GestureDetector(
child: UnicornAlertDialog(
title: Column(
children: <Widget>[
Container(
child: Image.asset('images/done.png'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
)
],
),
content: Text(
'You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 15.0)),
gradient: LinearGradient(
colors: <Color>[
Color(0xDD4a00e0),
Color(0xFF8e2de2),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
actions: <Widget>[ ]),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ThirdRoute()));
},
);
});
}
Unicorn alert dialog is used for background-color decoration, and since you cannot have gradient color in normal alert dialog, I used this.
code snippet
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return UnicornAlertDialog(
title: GestureDetector(
onTap: () { print("on tap title");},
child: Column(
children: <Widget>[
Container(
child: Image.asset('assets/images/background.jpg'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
)
],
),
),
content: GestureDetector(
onTap: () { print("on tap content");},
child: Text('You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 15.0)),
),
gradient: LinearGradient(
colors: <Color>[
Color(0xDD4a00e0),
Color(0xFF8e2de2),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
actions: <Widget>[
]
);
});
}
full code
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
class UnicornAlertDialog extends StatelessWidget {
const UnicornAlertDialog({
Key key,
#required this.gradient,
this.title,
this.titlePadding,
this.titleTextStyle,
this.content,
this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
this.contentTextStyle,
this.actions,
this.backgroundColor,
this.elevation,
this.semanticLabel,
this.shape,
}) : assert(contentPadding != null),
super(key: key);
final Gradient gradient;
final Widget title;
final EdgeInsetsGeometry titlePadding;
final TextStyle titleTextStyle;
final Widget content;
final EdgeInsetsGeometry contentPadding;
final TextStyle contentTextStyle;
final List<Widget> actions;
final Color backgroundColor;
final double elevation;
final String semanticLabel;
final ShapeBorder shape;
#override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = Theme.of(context);
final DialogTheme dialogTheme = DialogTheme.of(context);
final List<Widget> children = <Widget>[];
String label = semanticLabel;
if (title != null) {
children.add(Padding(
padding: titlePadding ?? EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0),
child: DefaultTextStyle(
style: titleTextStyle ?? dialogTheme.titleTextStyle ?? theme.textTheme.title,
child: Semantics(
child: title,
namesRoute: true,
container: true,
),
),
));
} else {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
label = semanticLabel;
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
label = semanticLabel ?? MaterialLocalizations.of(context)?.alertDialogLabel;
}
}
if (content != null) {
children.add(Flexible(
child: Padding(
padding: contentPadding,
child: DefaultTextStyle(
style: contentTextStyle ?? dialogTheme.contentTextStyle ?? theme.textTheme.subhead,
child: content,
),
),
));
}
if (actions != null) {
children.add(ButtonTheme.bar(
child: ButtonBar(
children: actions,
),
));
}
Widget dialogChild = IntrinsicWidth(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
),
);
if (label != null)
dialogChild = Semantics(
namesRoute: true,
label: label,
child: dialogChild,
);
return Dialog(
backgroundColor: backgroundColor,
gradient: gradient,
elevation: elevation,
shape: shape,
child: dialogChild,
);
}
}
class Dialog extends StatelessWidget {
const Dialog({
Key key,
this.gradient,
this.backgroundColor,
this.elevation,
this.insetAnimationDuration = const Duration(milliseconds: 100),
this.insetAnimationCurve = Curves.decelerate,
this.shape,
this.child,
}) : super(key: key);
final Color backgroundColor;
final double elevation;
final Duration insetAnimationDuration;
final Curve insetAnimationCurve;
final ShapeBorder shape;
final Widget child;
final Gradient gradient;
static const RoundedRectangleBorder _defaultDialogShape =
RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)));
static const double _defaultElevation = 24.0;
#override
Widget build(BuildContext context) {
final DialogTheme dialogTheme = DialogTheme.of(context);
return AnimatedPadding(
padding: MediaQuery.of(context).viewInsets + const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
duration: insetAnimationDuration,
curve: insetAnimationCurve,
child: MediaQuery.removeViewInsets(
removeLeft: true,
removeTop: true,
removeRight: true,
removeBottom: true,
context: context,
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 280.0),
child: Material(
color: backgroundColor ?? dialogTheme.backgroundColor ?? Theme.of(context).dialogBackgroundColor,
elevation: elevation ?? dialogTheme.elevation ?? _defaultElevation,
shape: shape ?? dialogTheme.shape ?? _defaultDialogShape,
type: MaterialType.card,
child: ClipRRect(
borderRadius: _defaultDialogShape.borderRadius,
child: Container(
decoration: BoxDecoration(
gradient: gradient
),
child: child,
),
),
),
),
),
),
);
}
}
_showDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return UnicornAlertDialog(
title: GestureDetector(
onTap: () { print("on tap title");},
child: Column(
children: <Widget>[
Container(
child: Image.asset('assets/images/background.jpg'),
),
const SizedBox(height: 15.0),
Container(
child: Text(
'Verify',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
),
),
)
],
),
),
content: GestureDetector(
onTap: () { print("on tap content");},
child: Text('You have successfully verified your mobile number',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white, fontSize: 15.0)),
),
gradient: LinearGradient(
colors: <Color>[
Color(0xDD4a00e0),
Color(0xFF8e2de2),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
actions: <Widget>[
]
);
});
}
Future<void> _ackAlert(BuildContext context) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Not in stock'),
content: const Text('This item is no longer available'),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
enum ConfirmAction { CANCEL, ACCEPT }
Future<ConfirmAction> _asyncConfirmDialog(BuildContext context) async {
return showDialog<ConfirmAction>(
context: context,
barrierDismissible: false, // user must tap button for close dialog!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Reset settings?'),
content: const Text(
'This will reset your device to its default factory settings.'),
actions: <Widget>[
FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop(ConfirmAction.CANCEL);
},
),
FlatButton(
child: const Text('ACCEPT'),
onPressed: () {
Navigator.of(context).pop(ConfirmAction.ACCEPT);
},
)
],
);
},
);
}
Future<String> _asyncInputDialog(BuildContext context) async {
String teamName = '';
return showDialog<String>(
context: context,
barrierDismissible: false, // dialog is dismissible with a tap on the barrier
builder: (BuildContext context) {
return AlertDialog(
title: Text('Enter current team'),
content: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
autofocus: true,
decoration: new InputDecoration(
labelText: 'Team Name', hintText: 'eg. Juventus F.C.'),
onChanged: (value) {
teamName = value;
},
))
],
),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: () {
Navigator.of(context).pop(teamName);
},
),
],
);
},
);
}
enum Departments { Production, Research, Purchasing, Marketing, Accounting }
Future<Departments> _asyncSimpleDialog(BuildContext context) async {
return await showDialog<Departments>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return SimpleDialog(
title: const Text('Select Departments '),
children: <Widget>[
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Production);
},
child: const Text('Production'),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Research);
},
child: const Text('Research'),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Purchasing);
},
child: const Text('Purchasing'),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Marketing);
},
child: const Text('Marketing'),
),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, Departments.Accounting);
},
child: const Text('Accounting'),
)
],
);
});
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: AppBar(
title: Text("Dialog"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
onPressed: () {
_showDialog(context);
},
child: const Text("Unicon Dialog"),
),
new RaisedButton(
onPressed: () {
_ackAlert(context);
},
child: const Text("Ack Dialog"),
),
new RaisedButton(
onPressed: () async {
final ConfirmAction action = await _asyncConfirmDialog(context);
print("Confirm Action $action" );
},
child: const Text("Confirm Dialog"),
),
new RaisedButton(
onPressed: () async {
final Departments deptName = await _asyncSimpleDialog(context);
print("Selected Departement is $deptName");
},
child: const Text("Simple dialog"),
),
new RaisedButton(
onPressed: () async {
final String currentTeam = await _asyncInputDialog(context);
print("Current team name is $currentTeam");
},
child: const Text("Input Dialog"),
),
],
),
),
);
}
}
void main() {
runApp(new MaterialApp(home: new MyApp()));
}