Related
users.dart
class UsersPage extends StatefulWidget {
const UsersPage(
BuildContext context, {
Key? key,
//required String title,
}) : super(key: key);
final String title = "Flutter DataTable";
#override
UsersPageState createState() => UsersPageState();
}
class UsersPageState extends State<UsersPage> {
var appBarHeight = AppBar().preferredSize.height;
PageController page = PageController();
//Create the (Global) Keys
final GlobalKey<FormState> _formKey = GlobalKey();
final GlobalKey<ScaffoldState> _drawerscaffoldkey =
GlobalKey<ScaffoldState>();
// late GlobalKey<ScaffoldState> _scaffoldKey;
final ScrollController horizontal = ScrollController(),
vertical = ScrollController();
// bool _isSortAsc = true;
int _currentSortColumn = 0;
bool _isAscending = true;
late bool _isUpdating;
late Users _selectedUser;
late String _titleProgress;
late List<Users> _users;
late TextEditingController _firstNameController;
late TextEditingController _lastNameController;
var list;
var index;
#override
void initState() {
super.initState();
_users = [];
_isUpdating = false;
_titleProgress = widget.title;
// _scaffoldKey = GlobalKey();
_firstNameController = TextEditingController();
_lastNameController = TextEditingController();
_getUsers();
}
static List<Users> parseResponse(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Users>((json) => Users.fromJson(json)).toList();
}
_setValues(Users user) {
_firstNameController.text = user.firstName;
_lastNameController.text = user.lastName;
setState(() {
_isUpdating = true;
});
}
_showProgress(String message) {
setState(() {
_titleProgress = message;
});
}
_getUsers() {
_showProgress('Loading User...');
Services.getUsers().then((users) {
setState(() {
// _users = users.cast<User>();
_users = users;
});
_showProgress(widget.title);
debugPrint("Length: ${users.length}");
});
}
_deleteUser(Users user) {
_showProgress('Deleting User...');
Services.deleteUser(user.userId).then((result) {
if ('success' == result) {
setState(() {
_users.remove(user);
});
_getUsers();
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
//============================== Appbar code here... ==============================
appBar: AppBar(
title: const Text("Users Page"),
leading: IconButton(
onPressed: () {
//on drawer menu pressed
if (_drawerscaffoldkey.currentState!.isDrawerOpen) {
//if drawer is open, then close the drawer
Navigator.pop(context);
} else {
_drawerscaffoldkey.currentState?.openDrawer();
//if drawer is closed then open the drawer.
}
},
icon: const Icon(Icons.menu),
),
actions: <Widget>[]),
//
body: Scaffold(
//second scaffold
key: _drawerscaffoldkey, //set gobal key defined above
//======================================== Drawer code here... ==========================
drawer: Row(children: [
SideMenu(
controller: page,
style: SideMenuStyle(
//showTooltip: false,
displayMode: SideMenuDisplayMode.auto,
hoverColor: Colors.blue[100],
selectedColor: Colors.blue,
selectedTitleTextStyle: const TextStyle(color: Colors.white),
selectedIconColor: Colors.white,
decoration: const BoxDecoration(
//borderRadius: BorderRadius.all(Radius.circular(10)),
color: Colors.black12,
border: Border(
right: BorderSide(
color: Colors.black,
width: 1.0,
),
),
boxShadow: [
BoxShadow(
color: Colors.white,
offset: Offset(0.0, 0.0),
blurRadius: 0.0,
spreadRadius: 0.0,
),
],
//backgroundBlendMode: BlendMode.color
), /*backgroundColor: Colors.black12*/
),
title: Column(
children: [
const SizedBox(
height: 10,
),
ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 150,
maxWidth: 150,
),
child: Image.asset(
'lib/assets/images/crm_logo.png',
),
),
const Divider(
indent: 8.0,
endIndent: 8.0,
),
],
),
items: [
SideMenuItem(
priority: 0,
title: 'Dashboard',
icon: const Icon(Icons.home),
onTap: () {
Navigator.pushReplacementNamed(context, '/AdminPage');
//sideMenu.changePage(const AdminPage(title: 'Dashboard'));
},
tooltipContent: "Dashboard",
),
SideMenuItem(
priority: 1,
title: 'Sales Team',
icon: const Icon(Icons.assignment_ind),
//GestureDetector(
onTap: () {
//const UsersPage(title: 'title');
Navigator.pushReplacementNamed(context, '/UsersPage');
},
//),
),
],
),
]),
//====================================== Main Body Code Here... =============================
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(
left: 320.0, top: 35.0, right: 35.0, bottom: 100.0),
child: Column(
children: <Widget>[
const Align(
alignment: Alignment.topLeft,
child: Text("Users View Page",
style: TextStyle(
fontSize: 24,
)),
),
Padding(
padding: const EdgeInsets.only(left: 400.0),
child: Row(
//mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
OutlinedButton(
style: TextButton.styleFrom(
foregroundColor: Colors.blueGrey,
side: const BorderSide(
color: Colors.black26,
width: 0.5,
),
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 20.0),
),
onPressed: () {
//_submit();
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const AddUserPage(
title: '',
)));
},
child: const Text("Add User",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
),
const SizedBox(
width: 100.0,
),
/* OutlinedButton(
style: TextButton.styleFrom(
foregroundColor: Colors.blueGrey,
side: const BorderSide(
color: Colors.black26,
width: 0.5,
),
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 20.0),
),
onPressed: () {
debugPrint('Edit Button Clicked');
},
child: const Text("Update User",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
),
const SizedBox(
width: 100.0,
), */
OutlinedButton(
style: TextButton.styleFrom(
foregroundColor: Colors.blueGrey,
side: const BorderSide(
color: Colors.black26,
width: 0.5,
),
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 20.0),
),
onPressed: () {
//_submit();
_getUsers();
/* Navigator.of(context).push(MaterialPageRoute(
builder: (context) => UsersPage(context))); */
},
child: const Text("Refresh",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
),
],
),
),
const SizedBox(
height: 30,
),
Scrollbar(
thickness: 5,
controller: horizontal,
trackVisibility: true,
child: SingleChildScrollView(
controller: horizontal,
scrollDirection: Axis.horizontal,
child: DataTable(
/* sortColumnIndex: _currentSortColumn,
sortAscending: _isSortAsc,
sortColumnIndex : 0,
sortAscending : 10.0, */
sortColumnIndex: _currentSortColumn,
sortAscending: _isAscending,
showCheckboxColumn: true,
// sortColumnIndex: 0,
// sortAscending: true,
/* selected: true,
showEditIcon: true, */
border: TableBorder.all(
width: 1.0,
color: Colors.black45,
),
headingRowColor: MaterialStateProperty.resolveWith(
(states) => Colors.blueGrey),
headingTextStyle: TextStyle(
fontWeight: FontWeight.bold, color: Colors.white),
columns: const [
/* DataColumn(
label: Text(
"LINE NO. #",
),
numeric: false,
tooltip: "This is the Line No."), */
DataColumn(
label: Text("USER ID"),
numeric: false,
tooltip: "This is the user id",
/* onSort: (int columnIndex, bool ascending) {
setState(() {
_currentSortColumn = columnIndex;
if (_isAscending == true) {
_isAscending = false;
// sort the users list in Ascending, order by ID
_users.sort((userA, userB) =>
userB.userId.compareTo(userA.userId));
} else {
_isAscending = true;
// sort the users list in Descending, order by ID
_users.sort((userA, userB) =>
userA.userId.compareTo(userB.userId));
}
});
} */
),
DataColumn(
label: Text(
"FIRST NAME",
),
numeric: false,
tooltip: "This is the last name"),
DataColumn(
label: Text("LAST NAME"),
numeric: false,
tooltip: "This is the last name"),
DataColumn(
label: Text("ACTIONS"),
numeric: false,
tooltip: "Related Actions"),
],
rows: _users
.map(
(user) => DataRow(
cells: [
DataCell(
Text(user.userId),
/* onTap: () {
debugPrint("Tapped ${user.userId}");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AddUpdateUserPage()));
_setValues(user);
_selectedUser = user;
}, */
),
DataCell(
Text(
user.firstName,
),
onTap: () {
debugPrint("Tapped ${user.firstName}");
_setValues(user);
_selectedUser = user;
},
),
DataCell(
Text(
user.lastName /* .toUpperCase() */,
),
onTap: () {
debugPrint("Tapped ${user.lastName}");
_setValues(user);
_selectedUser = user;
},
),
DataCell(
Row(
children: [
IconButton(
icon: Icon(Icons.edit),
tooltip: "Edit",
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AddUpdateUserPage(
list: list,
index: index),
),
);
debugPrint("Edit Mode Enabled...");
},
),
IconButton(
icon: Icon(Icons.delete),
tooltip: "Delete",
onPressed: () {
_deleteUser(user);
debugPrint("Data Row Deleted...");
},
),
],
),
/* onTap: () {
// print("Tapped 'Delete' Action");
debugPrint("Data Row Deleted...");
}, */
),
],
),
)
.toList(),
),
),
),
],
),
),
),
));
}
}
//============================= Services Class Code =================================
class Services {
static const ROOT = 'http://localhost/crm/user_actions.php';
static const String _GET_ACTION = 'GET_ALL';
static const String _ADD_USER_ACTION = 'ADD_USER';
static const String _UPDATE_USER_ACTION = 'UPDATE_USER';
static const String _DELETE_USER_ACTION = 'DELETE_USER';
static Future<List<Users>> getUsers() async {
try {
// var map = new Map<String, dynamic>();
var map = <String, dynamic>{};
map["action"] = _GET_ACTION;
final response = await http.post(Uri.parse(ROOT), body: map);
debugPrint("getUsers >> Response:: ${response.body}");
if (response.statusCode == 200) {
List<Users> list = parsePhotos(response.body);
return list;
} else {
// throw List<Employee>();
throw <Users>[];
}
} catch (e) {
// return List<Employee>();
return <Users>[];
}
}
static List<Users> parsePhotos(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Users>((json) => Users.fromJson(json)).toList();
}
static Future<String> addUser(
String firstName,
String lastName,
) async {
try {
// var map = new Map<String, dynamic>();
var map = <String, dynamic>{};
map["action"] = _ADD_USER_ACTION;
map["firstname"] = firstName;
map["lastname"] = lastName;
final response = await http.post(Uri.parse(ROOT), body: map);
debugPrint("addUser >> Response:: ${response.body}");
return response.body;
} catch (e) {
return 'error';
}
}
static Future<String> updateUser(
/* String userId, */
String firstName,
String lastName,
) async {
try {
// var map = new Map<String, dynamic>();
var map = <String, dynamic>{};
map["action"] = _UPDATE_USER_ACTION;
// map["user_id"] = userId;
map["firstname"] = firstName;
map["lastname"] = lastName;
final response = await http.post(Uri.parse(ROOT), body: map);
debugPrint("deleteUser >> Response:: ${response.body}");
return response.body;
} catch (e) {
return 'error';
}
}
static Future<String> deleteUser(String userId) async {
try {
// var map = new Map<String, dynamic>();
var map = <String, dynamic>{};
map["action"] = _DELETE_USER_ACTION;
map["user_id"] = userId;
final response = await http.post(Uri.parse(ROOT), body: map);
debugPrint("deleteUser >> Response:: ${response.body}");
return response.body;
} catch (e) {
return 'error';
}
}
}
class Users {
String userId;
String firstName;
String lastName;
Users({
required this.userId,
required this.firstName,
required this.lastName,
});
factory Users.fromJson(Map<String, dynamic> json) {
return Users(
userId: json['user_id'],
firstName: json['firstname'] as String,
lastName: json['lastname'] as String,
);
}
}
Here i wrote the code for CRUD operations on Data table with flutter by using php, mysql. Here the Create, Read, Delete functionalities are working fine except update functionality. Actually, i'm trying to get the existing data from one page to another page of texe form fields to update the existing data (which is stored in DB). But, i got
Expected a value of type 'List', but got one of type 'Null'`
Error like this :
I am developing an application in which user can contact each other via video calls. I have setup my server on railway by following the Agora documentation. If anyone has any suggestions or know what I am doing wrong please do let me know. I have tried leaving the token empty ('') but it still gives invalid token error. The token is getting generated successfully but when users join the call. Remote view is not showing up even though the onUserJoined callback is getting triggered perfectly on both caller and receiver side.
This is the call screen code which will enable users to contact with each other
// ignore_for_file: prefer_typing_uninitialized_variables, use_build_context_synchronously
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../../constants/constants.dart';
import '../../global/firebase_ref.dart';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wakelock/wakelock.dart';
import '../../methods/call_methods.dart';
import '../../models/call_model.dart';
import '../../services/app_services.dart';
import '../../services/connectivity_services.dart';
import '../../services/user_services.dart';
import '../../widgets/custom_images.dart';
import '../../widgets/custom_widgets.dart';
class VideoCallScreen extends StatefulWidget {
const VideoCallScreen(this.call, {Key? key}) : super(key: key);
final CallModel call;
#override
State<VideoCallScreen> createState() => _VideoCallScreenState();
}
class _VideoCallScreenState extends State<VideoCallScreen> {
final _users = <int>[];
final _infoStrings = <String>[];
bool muted = false;
RtcEngine? _engine;
bool isspeaker = true;
bool isalreadyendedcall = false;
String current = Get.find<UserServices>().adminid;
CollectionReference? reference;
String token = '';
Stream<DocumentSnapshot>? stream;
#override
void dispose() {
_users.clear();
_engine!.leaveChannel();
_engine!.release();
super.dispose();
}
getToken() async {
final url = Uri.parse(
'https://agora-token-service-production-59a1.up.railway.app/rtc/${widget.call.channelid}/1/uid/0',
);
Get.log('Token URL $url');
final response = await http.get(url);
debugPrint('Response: $response');
if (response.statusCode == 200) {
setState(() {
token = response.body;
token = jsonDecode(token)['rtcToken'];
Get.log('token: $token');
});
} else {
Get.log('Failed to fetch the token');
}
}
#override
void initState() {
initialize();
super.initState();
if (widget.call.by == current) {
reference = userRef.doc(widget.call.receiver!.id).collection('History');
stream = reference!.doc(widget.call.timeepoch.toString()).snapshots();
} else {
reference = adminRef.doc(widget.call.caller!.id).collection('History');
stream = reference!.doc(widget.call.timeepoch.toString()).snapshots();
}
}
Future<void> initialize() async {
try {
await [Permission.microphone, Permission.camera].request();
await getToken();
if (Get.find<AppServices>().appid.isEmpty) {
setState(() {
_infoStrings.add(
'Agora_APP_IDD missing, please provide your Agora_APP_IDD in app_constant.dart',
);
_infoStrings.add('Agora Engine is not starting');
});
return;
}
await _initAgoraRtcEngine();
_addAgoraEventHandlers();
VideoEncoderConfiguration configuration = const VideoEncoderConfiguration(
dimensions: VideoDimensions(height: 1920, width: 1080),
);
await _engine!.setVideoEncoderConfiguration(configuration);
Get.log('Channel id: ${widget.call.channelid}');
await _engine!.joinChannel(
token: token,
channelId: widget.call.channelid!,
uid: 0,
options: const ChannelMediaOptions(),
);
} catch (e) {
Get.log('Catch: $e');
}
}
Future<void> _initAgoraRtcEngine() async {
_engine = createAgoraRtcEngine();
await _engine!.initialize(
RtcEngineContext(
appId: Get.find<AppServices>().appid,
channelProfile: ChannelProfileType.channelProfileCommunication,
),
);
// _engine = await RtcEngine.create(Get.find<AppServices>().agoraid);
await _engine!.enableVideo();
await _engine!.enableAudio();
await _engine!.enableLocalVideo(true);
await _engine!.enableLocalAudio(true);
await _engine!.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
Get.log('---engine----');
}
var remoteid;
void _addAgoraEventHandlers() {
_engine!.registerEventHandler(
RtcEngineEventHandler(
onError: (code, value) {
setState(() {
final info = 'onErrorCode: $code';
_infoStrings.add(info);
Get.log(info);
final infp = 'onError: $value';
_infoStrings.add(infp);
Get.log(infp);
});
},
onJoinChannelSuccess: (channel, elapsed) {
setState(() {
final info = 'onJoinChannel: $channel';
_infoStrings.add(info);
Get.log(info);
});
if (widget.call.caller!.id == current) {
adminRef
.doc(widget.call.caller!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'TYPE': 'OUTGOING',
'ISVIDEOCALL': widget.call.video,
'PEER': widget.call.receiver!.id,
'TARGET': widget.call.receiver!.id,
'TIME': widget.call.timeepoch,
'DP': widget.call.receiver!.picture,
'ISMUTED': false,
'ISJOINEDEVER': false,
'STATUS': 'calling',
'STARTED': null,
'ENDED': null,
'CALLERNAME': widget.call.caller!.name,
'CHANNEL': channel.channelId,
'UID': channel.localUid,
}, SetOptions(merge: true)).then(
(value) => Get.log('added'),
);
userRef
.doc(widget.call.receiver!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'TYPE': 'INCOMING',
'ISVIDEOCALL': widget.call.video,
'PEER': widget.call.caller!.id,
'TARGET': widget.call.receiver!.id,
'TIME': widget.call.timeepoch,
'DP': widget.call.caller!.picture,
'ISMUTED': false,
'ISJOINEDEVER': true,
'STATUS': 'missedcall',
'STARTED': null,
'ENDED': null,
'CALLERNAME': widget.call.caller!.name,
'CHANNEL': channel.channelId,
'UID': channel.localUid,
}, SetOptions(merge: true));
}
Wakelock.enable();
},
onLeaveChannel: (connection, stats) {
// setState(() {
_infoStrings.add('onLeaveChannel');
_users.clear();
// });
if (isalreadyendedcall == false) {
adminRef
.doc(widget.call.caller!.id!)
.collection("History")
.doc(widget.call.timeepoch.toString())
.set({
'STATUS': 'ended',
'ENDED': DateTime.now(),
'ISMUTED': false,
'UID': -1,
}, SetOptions(merge: true));
userRef
.doc(widget.call.receiver!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'STATUS': 'ended',
'ENDED': DateTime.now(),
'ISMUTED': false,
'UID': -1,
}, SetOptions(merge: true));
// //----------
// userRef
// .doc(widget.call.receiver!.id)
// .collection('recent')
// .doc(widget.call.id)
// .set({
// 'id': widget.call.caller!.id,
// 'ENDED': DateTime.now().millisecondsSinceEpoch,
// 'CALLERNAME': widget.call.receiver!.name,
// }, SetOptions(merge: true));
}
Wakelock.disable();
},
onUserJoined: (connection, uid, elapsed) {
setState(() {
final info = 'userJoined: $uid';
_infoStrings.add(info);
_users.add(uid);
Get.log(info);
remoteid = uid;
Get.log(remoteid);
});
startTimerNow();
if (Get.find<UserServices>().adminid == widget.call.caller!.id) {
adminRef
.doc(widget.call.caller!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'STARTED': DateTime.now(),
'STATUS': 'pickedup',
'ISJOINEDEVER': true,
}, SetOptions(merge: true));
userRef
.doc(widget.call.receiver!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'STARTED': DateTime.now(),
'STATUS': 'pickedup',
}, SetOptions(merge: true));
}
Wakelock.enable();
},
onUserOffline: (connection, uid, elapsed) {
setState(() {
final info = 'userOffline: $uid';
_infoStrings.add(info);
_users.remove(uid);
Get.log(info);
remoteid = null;
});
if (isalreadyendedcall == false) {
adminRef
.doc(widget.call.caller!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'STATUS': 'ended',
'ENDED': DateTime.now(),
'ISMUTED': false,
'UID': -1,
}, SetOptions(merge: true));
userRef
.doc(widget.call.receiver!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'STATUS': 'ended',
'ENDED': DateTime.now(),
'ISMUTED': false,
'UID': -1,
}, SetOptions(merge: true));
//----------
// userRef
// .doc(widget.call.receiver!.id)
// .collection('recent')
// .doc(widget.call.id)
// .set({
// 'id': widget.call.caller!.id,
// 'ENDED': DateTime.now().millisecondsSinceEpoch,
// 'CALLERNAME': widget.call.receiver!.name,
// }, SetOptions(merge: true));
}
},
onFirstRemoteVideoFrame: (connection, uid, width, height, elapsed) {
setState(() {
final info = 'firstRemoteVideo: $uid ${width}x $height';
_infoStrings.add(info);
Get.log(info);
});
},
onTokenPrivilegeWillExpire: (connection, string) async {
await getToken();
await _engine!.renewToken(token);
},
),
);
}
void onCallEnd(BuildContext context) async {
await CallMethods.endCall(call: widget.call);
DateTime now = DateTime.now();
if (isalreadyendedcall == false) {
await adminRef
.doc(widget.call.caller!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'STATUS': 'ended',
'ENDED': now,
'ISMUTED': false,
"UID": -1,
}, SetOptions(merge: true));
await userRef
.doc(widget.call.receiver!.id!)
.collection('History')
.doc(widget.call.timeepoch.toString())
.set({
'STATUS': 'ended',
'ENDED': now,
'ISMUTED': false,
'UID': -1,
}, SetOptions(merge: true));
// //----------
// userRef
// .doc(widget.call.receiver!.id)
// .collection('recent')
// .doc(widget.call.id)
// .set({
// 'id': widget.call.caller!.id,
// 'ENDED': DateTime.now().millisecondsSinceEpoch,
// 'CALLERNAME': widget.call.receiver!.name,
// }, SetOptions(merge: true));
}
Wakelock.disable();
Navigator.pop(context);
}
Widget callView({
String status = 'calling',
bool muted = false,
int? remoteuid,
}) {
var w = MediaQuery.of(context).size.width;
var h = MediaQuery.of(context).size.height;
return Container(
alignment: Alignment.center,
decoration: status == 'pickedup'
? null
: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: providerImage(
widget.call.caller!.id == current
? widget.call.receiver!.picture ?? ''
: widget.call.caller!.picture ?? '',
),
),
),
child: Container(
color: status == 'pickedup' ? null : Colors.white.withOpacity(0.3),
child: Stack(
alignment: Alignment.center,
children: [
status != 'pickedup'
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: w,
height: h / 5,
alignment: Alignment.center,
margin: EdgeInsets.only(
top: MediaQuery.of(context).padding.top),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.lock_rounded,
size: 17,
color: Colors.white38,
),
SizedBox(width: 6),
Text(
'End-to-end encrypted',
style: TextStyle(
color: Colors.white38,
fontWeight: FontWeight.w400,
fontFamily: AppStrings.opensans,
),
),
],
).marginOnly(top: 50, bottom: 7),
SizedBox(
width: w / 1.1,
child: Text(
widget.call.caller!.id ==
Get.find<UserServices>().adminid
? widget.call.receiver!.name!
: widget.call.caller!.name!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 27,
fontFamily: AppStrings.opensans,
),
),
),
],
),
),
Text(
status == 'calling'
? widget.call.receiver!.id ==
Get.find<UserServices>().adminid
? 'Connecting...'
: 'Calling...'
: status == 'pickedup'
? '$hoursStr : $minutesStr: $secondsStr'
: status == 'ended'
? 'Call Ended'
: status == 'rejected'
? 'Rejected'
: 'Please wait...',
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18,
fontFamily: AppStrings.opensans,
),
).marginOnly(bottom: 16, top: 10),
Stack(
children: [
widget.call.caller!.id ==
Get.find<UserServices>().adminid
? status == 'ended' || status == 'rejected'
? Container(
height: w + (w / 11),
width: w,
color: Colors.white12,
child: Icon(
status == 'ended'
? Icons.person_off
: status == 'rejected'
? Icons.call_end_rounded
: Icons.person,
size: 140,
),
)
: Container()
: status == 'ended' || status == 'rejected'
? Container(
height: w + (w / 11),
width: w,
color: Colors.white12,
child: Icon(
status == 'ended'
? Icons.person_off
: status == 'rejected'
? Icons.call_end_rounded
: Icons.person,
size: 140,
),
)
: Container(),
Positioned(
bottom: 20,
child: SizedBox(
width: w,
height: 20,
child: Center(
child: status == 'pickedup'
? muted == true
? const Text(
'Muted',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
fontFamily: AppStrings.opensans,
),
)
: const SizedBox(height: 0)
: const SizedBox(height: 0),
),
),
),
],
),
SizedBox(height: h / 6),
],
)
: _engine == null
? SizedBox()
: SizedBox(
child: AgoraVideoView(
controller: VideoViewController.remote(
rtcEngine: _engine!,
canvas: VideoCanvas(uid: remoteuid),
connection: RtcConnection(
channelId: widget.call.channelid!,
),
),
),
),
if (status == 'pickedup')
Positioned(
top: 150,
child: Text(
'$hoursStr: $minutesStr: $secondsStr',
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18,
color: Colors.white,
fontFamily: AppStrings.opensans,
),
),
),
if (status != 'ended' || status != 'rejected')
_engine == null
? SizedBox()
: Align(
alignment: Alignment.bottomRight,
child: SizedBox(
width: 200,
height: 200,
child: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine!,
canvas: const VideoCanvas(uid: 0),
),
),
),
),
],
),
),
);
}
onToggleMute() {
setState(() {
muted = !muted;
});
_engine!.muteLocalAudioStream(muted);
reference!
.doc(widget.call.timeepoch.toString())
.set({'ISMUTED': muted}, SetOptions(merge: true));
}
onSwitchCamera() => setState(() => _engine!.switchCamera());
Widget toolbar({String status = 'calling'}) {
return Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.symmetric(vertical: 35),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
status != 'ended' && status != 'rejected'
? SizedBox(
width: 65.67,
child: RawMaterialButton(
onPressed: onToggleMute,
shape: const CircleBorder(),
elevation: 2.0,
fillColor: muted ? Colors.blueAccent : Colors.white,
padding: const EdgeInsets.all(12.0),
child: Icon(
muted ? Icons.mic_off : Icons.mic,
color: muted ? Colors.white : Colors.blueAccent,
size: 22.0,
),
),
)
: const SizedBox(height: 42, width: 65.67),
SizedBox(
width: 65.67,
child: RawMaterialButton(
onPressed: () async {
Get.log('--on call end---');
setState(() {
isalreadyendedcall =
status == 'ended' || status == 'rejected' ? true : false;
onCallEnd(context);
});
},
shape: const CircleBorder(),
elevation: 2.0,
fillColor: status == 'ended' || status == 'rejected'
? Colors.black
: Colors.redAccent,
padding: const EdgeInsets.all(15.0),
child: Icon(
status == 'ended' || status == 'rejected'
? Icons.close
: Icons.call,
color: Colors.white,
size: 35.0,
),
),
),
status == 'ended' || status == 'rejected'
? const SizedBox(width: 65.67)
: SizedBox(
width: 65.67,
child: RawMaterialButton(
onPressed: onSwitchCamera,
shape: const CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(12.0),
child: const Icon(
Icons.switch_camera,
color: Colors.blueAccent,
size: 20.0,
),
),
),
],
),
);
}
Widget panel() {
return Container(
padding: const EdgeInsets.symmetric(vertical: 48),
alignment: Alignment.bottomCenter,
child: FractionallySizedBox(
heightFactor: 0.5,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 48),
child: ListView.builder(
reverse: true,
itemCount: _infoStrings.length,
itemBuilder: (BuildContext context, int index) {
if (_infoStrings.isEmpty) return const SizedBox();
return Padding(
padding:
const EdgeInsets.symmetric(vertical: 3, horizontal: 10),
child: Text(_infoStrings[index]),
);
},
),
),
),
);
}
#override
Widget build(BuildContext context) {
return Obx(
() => Get.find<ConnectivityService>().connectionStatus.value ==
ConnectivityResult.none
? const DisconnectedWidget()
: Scaffold(
body: Stack(
children: [
_engine == null
? Center(
child: Stack(
children: [callView(), panel(), toolbar()],
),
)
: StreamBuilder<DocumentSnapshot>(
stream: stream as Stream<DocumentSnapshot>,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.data!.data() != null &&
snapshot.data != null) {
var doc = snapshot.data!;
Get.log(doc.toString());
return Center(
child: Stack(
children: [
callView(
status: doc['STATUS'],
muted: doc['ISMUTED'],
remoteuid: doc['UID'],
),
panel(),
toolbar(status: doc['STATUS']),
],
),
);
}
return Center(
child: Stack(
children: [callView(), panel(), toolbar()],
),
);
},
),
],
),
),
);
}
}
I didn't set it up with a rails server but it should work the same. On the flutter side im calling a function at the very first point:
final token = await createToken(channelName, userId);
channel name to identify the channel for the users and a userId of my user who should be able to join the channel.
Future<dynamic> createToken(String channelName, int uid) async {
try {
//404
// final response = await dio.get('${url}/api/video/create-token?agChannelName=$channelName&agRole=$role&agUid=$uid&agExpireTime=$expireTime');
final response = await dio.get(
'${url}/api/video/create-token?agChannelName=$channelName&agUid=$uid');
print('res is ${response.data["token"]}');
return response.data["token"];
} on DioError catch (e) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx and is also not 304.
if (e.response != null) {
//print(HttpException(e.response!.data["message"]));
return e.response!.data;
//print(e.response!.headers);
//print(e.response!.requestOptions);
} else {
// Something happened in setting up or sending the request that triggered an Error
//print(e.requestOptions);
print('get events: ${e.message}');
}
}
}
On my server side where im using a javascript framework, im doing the following:
...
const token = RtcTokenBuilder.buildTokenWithUid(
process.env.AGORA_APP_ID,
process.env.AGORA_APP_CERTIFICATE,
channelName,
uid,
RtcRole.PUBLISHER,
privilegeExpireTime
);
console.log(token)
return res.status(201).json({ token: token });
For that im using the agora-access-token library on npm https://www.npmjs.com/package/agora-access-token
First I want to introduce the pages.
Form Page
This is where I fill a form. When I click save as draft it will save the form data to the SQLite database. For now, I haven't implemented SQLite yet. Anyway, But I have a list that buffers the form input. When you click on Save as draft, I create a FormData object and add that data to a list and provide that list with Provider.
Form Page Code
class FormPage extends StatefulWidget {
FormData? formData = FormData();
FormPage({Key? key, this.formData}) : super(key: key);
#override
State<FormPage> createState() => _HomePageState();
}
class _HomePageState extends State<FormPage> {
String? isEmpty(String val) {
String? text;
setState(() {
if (val.isEmpty) {
text = 'Boş bırakılamaz';
}
});
return text;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xffffe0b2),
appBar: AppBar(
// backgroundColor: Colors.teal.shade400,
title: const Text('Hasta Etkileşim Kaydı'),
centerTitle: true,
backgroundColor: const Color(0xffffb74d),
elevation: 0,
),
body: SafeArea(
child: FutureBuilder(
future: readJsonData(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else if (snapshot.hasData) {
var items = snapshot.data as List<dynamic>;
var listStajTuru = items[0].stajTuruItems;
var listCinsiyet = items[0].cinsiyetItems;
var listEtkilesim = items[0].etkilesimTuruItems;
var listKapsam = items[0].kapsamItems;
var listOrtam = items[0].ortamItems;
var listDoktor = items[0].doktorItems;
return Form(
key: _formKey,
child: ListView(
shrinkWrap: true,
children: [
myDropDownContainer(_valueStajTuru, listStajTuru,
hintTextStajTuru, onChangedStajTuru),
myDropDownContainer(_valueDoktor, listDoktor,
hintTextDoktor, onChangedDoktor),
myDropDownContainer(_valueCinsiyet, listCinsiyet,
hintTextCinsiyet, onChangedCinsiyet),
myDropDownContainer(_valueEtkilesim, listEtkilesim,
hintTextEtkilesim, onChangedEtkilesim),
myDropDownContainer(_valueKapsam, listKapsam,
hintTextKapsam, onChangedKapsam),
myDropDownContainer(_valueOrtam, listOrtam,
hintTextOrtam, onChangedOrtam),
myTextFieldRow(20, "Kayıt No: ", 10,
_formData.setKayitNo, isEmpty),
myTextFieldRow(
20, "Hastanın Yaşı:", 3, _formData.setYas, isEmpty),
myTextFieldRow(
20, "Şikayet:", 10, _formData.setSikayet, isEmpty),
myTextFieldRow(50, "Ayırıcı Tanı:", 50,
_formData.setAyiriciTani, isEmpty),
myTextFieldRow(50, "Kesin Tanı:", 50,
_formData.setKesinTani, isEmpty),
myTextFieldRow(50, "Tedavi Yöntemi:", 100,
_formData.setTedaviYontemi, isEmpty),
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
submitButton(context, "Sava as Draft"),
submitButton(context, "GÖNDER"),
],
),
],
),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
})),
);
}
//Kullanıcı kaydet butonuna basarsa local olarak kaydedecek
Container submitButton(BuildContext context, String title) {
return Container(
width: 120,
height: 50,
margin: const EdgeInsets.all(12),
child: TextButton(
onPressed: () {
setState(() {
if (_formKey.currentState!.validate()) {
Provider.of<FormAdd>(context, listen: false)
.addNewFormToList(_formData);
alertDialog(context).then((_) =>_formKey.currentState!.reset());
}
});
},
child: Text(
title,
style: kTextStyle.copyWith(
fontSize: 14, color: Colors.white, fontWeight: FontWeight.bold),
),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
const Color(0xffffa726),
),
),
),
);
}
//DropDownWidget
Container myDropDownContainer(String initialVal, List<String> listItems,
String text, Function myFunc) {
return Container(
margin: const EdgeInsets.all(8),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 120,
child: Text(
text,
style: kTextStyle,
),
),
const SizedBox(
width: 20,
),
Expanded(
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.orangeAccent,
borderRadius: BorderRadius.circular(5)),
child: DropdownButtonFormField<String>(
// autovalidateMode: AutovalidateMode.always,
//menuMaxHeight: 300,
validator: (value) {
if (value!.isEmpty) {
return "485s4a8sd4as85";
}
},
decoration: const InputDecoration(border: InputBorder.none),
isExpanded: true,
//onTap: () => myFunc,
//borderRadius: BorderRadius.circular(5),
value: initialVal,
icon: const Icon(
Icons.arrow_downward,
color: Colors.black38,
),
iconSize: 24,
elevation: 16,
dropdownColor: Colors.deepOrange,
style: kTextStyle.copyWith(color: Colors.black),
onChanged: (val) => myFunc(val),
items: listItems.map<DropdownMenuItem<String>>((String? val) {
return DropdownMenuItem(
//TODO: Set default values
value: val == null ? val = initialVal : val = val,
child: Text(
val,
style: kTextStyle.copyWith(color: Colors.black),
),
);
}).toList(),
),
),
)
],
),
);
}
//TextFieldWidget
Row myTextFieldRow(double height, String text, int? maxLength,
Function function, Function regexFunction) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: 80,
height: height,
child: Text(
text,
style: kTextStyle.copyWith(color: Colors.black54),
),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextFormField(
validator: (value) => regexFunction(value),
// focusNode: FocusNode(),
onChanged: (input) {
function(input);
},
autofocus: false,
textAlignVertical: TextAlignVertical.bottom,
style: kTextStyle.copyWith(fontSize: 16),
maxLength: maxLength,
maxLines: null, //TODO:Arrange maxlines for the inputs
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.fromLTRB(0, 0, 0, height),
border: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.green),
borderRadius: BorderRadius.all(Radius.circular(3))),
// labelStyle:kTextStyle.copyWith(fontSize: 16, color: Colors.white54),
),
),
),
],
);
}
Future<List<FormContent>> readJsonData() async {
final jsonData =
await rootBundle.rootBundle.loadString('assets/json/formdata.json');
return [
for (final e in json.decode(jsonData)) FormContent.fromJson(e),
];
}
final _formKey = GlobalKey<FormState>();
final FormData _formData = FormData();
String _valueEtkilesim = "Gözlem";
String _valueKapsam = "Öykü";
String _valueOrtam = "Poliklinik";
String _valueDoktor = "Diğer";
String _valueStajTuru = "Ortopedi";
String _valueCinsiyet = "Erkek"; // initial value
final String hintTextCinsiyet = "Cinsiyet:";
final String hintTextStajTuru = "Staj Türü:";
final String hintTextEtkilesim = "Etkileşim Türü:";
final String hintTextKapsam = "Kapsam:";
final String hintTextOrtam = "Gerçekleştiği Ortam:";
final String hintTextDoktor = "Klinik Eğitici:";
void onChangedCinsiyet(String? newVal) {
setState(() {
_valueCinsiyet = newVal!;
_formData.setCinsiyet(_valueCinsiyet);
});
}
void onChangedStajTuru(String newVal) {
setState(() {
_valueStajTuru = newVal;
_formData.setStajTuru(newVal);
});
}
void onChangedEtkilesim(String newVal) {
setState(() {
_valueEtkilesim = newVal;
_formData.setEtkilesimTuru(newVal);
});
}
void onChangedKapsam(String newVal) {
setState(() {
_valueKapsam = newVal;
_formData.setKapsam(newVal);
});
}
void onChangedOrtam(String newVal) {
setState(() {
_valueOrtam = newVal;
_formData.setOrtam(newVal);
});
}
void onChangedDoktor(String newVal) {
setState(() {
_valueDoktor = newVal;
_formData.setDoktor(newVal);
});
}
}
Assume, we saved the form as a draft. Now, It is in the DraftsPage.
Drafts Page
Everything is alright up to now. I have a list of the saved in draft forms. When I click on any list item I could get the form information (3rd picture).
Here is the question.
When I click on the Edit button. I want to renavigate to the Form Page.However this time I want to fill the places with the relevant data. For instance, this time Kayıt No: TextField shouldn't be empty it must be filled with 555, because it was saved to draft. And unfilled places must be blank. I know I need to provide an object of the form(formData) but, I couldn't manage it. When I click on the Edit button. I should provide that object and use it in the Form Page to fill those forms. But, how?
Draft Code
class Drafts extends StatelessWidget {
#override
Widget build(BuildContext context) {
void pushToFormPage(FormData formData) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FormView(
formData: formData,
)));
}
List<FormData> _forms = Provider.of<FormAdd>(context).getForms();
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: const Text("Taslaklar"),
centerTitle: true,
),
body: ListView.separated(
separatorBuilder: (BuildContext context, int index) => const Divider(
height: 5,
color: Colors.blueAccent,
),
itemCount: _forms.length,
itemBuilder: (BuildContext context, int index) {
return studentListViewInstanceContainer(
const Color.fromARGB(200, 200, 130, 240),
pushToFormPage,
_forms[index], context);
},
),
);
}
}
GestureDetector studentListViewInstanceContainer(
Color color, Function function, FormData formData,BuildContext context) {
return GestureDetector(
onTap: () => function(formData),
child: Container(
height: 100,
color: color,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Kayıt No: ${formData.getKayitNo()}',
style: kTextStyle,
),
Text(
'Tarih: ${formData.getKayitNo()}',
style: kTextStyle.copyWith(fontSize: 15),
) //TODO: implement DateT
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(onPressed: (){
// taslağa kaydedilmiş bilgiler tekrar yüklenecek.
FormData data= Provider.of<FormData>(context,listen: false);
Navigator.push(context, MaterialPageRoute(builder: (context)=>FormPage(formData: data,)));
},
child:const Text('Edit'),
),
const SizedBox(width: 5,),
ElevatedButton(onPressed: (){
},
child: const Text('Delete'),
),
],
),
],
),
),
);
}
This is my FormData model class.
class FormData with ChangeNotifier{
String? _kayitNo;
String? _stajTuru;
String? _doktor; //TODO: Convert string to AttendingPhysician type object
// late AttendingPhysician klinikEgitici;
String? _yas; // TODO: Convert to string later
String? _cinsiyet;
String? _sikayet;
String? _ayiriciTani;
String? _kesinTani;
String? _tedaviYontemi;
String? _etkilesimTuru;
String? _kapsam;
String? _gerceklestigiOrtam;
//DateTime tarih;
String getKayitNo()=>_kayitNo!;
String getStajTuru()=>_stajTuru!;
String getDoktor()=>_doktor!;
String getYas()=>_yas!;
String getCinsiyet()=>_cinsiyet!;
String getSikayet()=>_sikayet!;
String getAyiriciTani()=>_ayiriciTani!;
String getKesinTani()=>_kesinTani!;
String getTedaviYontemi()=>_tedaviYontemi!;
String getEtkilesimTuru()=>_etkilesimTuru!;
String getKapsam()=>_kapsam!;
String getOrtam()=>_gerceklestigiOrtam!;
void setStajTuru(String stajTuru) {
_stajTuru = stajTuru;
notifyListeners();
}
void setCinsiyet(String cinsiyet) {
_cinsiyet = cinsiyet;
notifyListeners();
}
void setEtkilesimTuru(String etkilesim) {
_etkilesimTuru = etkilesim;
notifyListeners();
}
void setKapsam(String kapsam) {
_kapsam = kapsam;
notifyListeners();
}
void setOrtam(String ortam) {
_gerceklestigiOrtam = ortam;
notifyListeners();
}
void setKayitNo(String kayitNo) {
_kayitNo = kayitNo;
notifyListeners();
}
void setYas(String yas) {
_yas = yas;
notifyListeners();
}
void setSikayet(String sikayet) {
_sikayet = sikayet;
notifyListeners();
}
void setAyiriciTani(String ayiriciTani) {
_ayiriciTani = ayiriciTani;
notifyListeners();
}
void setKesinTani(String kesinTani) {
_kesinTani = kesinTani;
notifyListeners();
}
void setTedaviYontemi(String tedaviYontemi) {
_tedaviYontemi = tedaviYontemi;
notifyListeners();
}
void setDoktor(String doktor){
_doktor=doktor;
notifyListeners();
}
}
To make it easier to understand the question. This process is the same as saving mail as a draft. When you want to edit the mail, the unfinished mail will show up not an empty mail.
I integrated Agora in Flutter Application with the help of agora_rtc_engine 4.0.7, but upon making a video call for the very first time the local camera view is not working. When we disconnect the call and call back again then everything works fine, the local camera view started working.
Again when we kill the application from Phone memory and rerun it then also the same problem occurs for the first time.
VideoCallingScreen.dart file
class VideoCallingScreen extends StatefulWidget {
String doctorId;
String bookingId;
VideoCallingScreen(this.doctorId, this.bookingId);
#override
_VideoCallingScreenState createState() => _VideoCallingScreenState();
}
class _VideoCallingScreenState extends State<VideoCallingScreen> {
int? _remoteUid;
RtcEngine? _engine;
bool isJoined = false,
switchCamera = true,
switchRender = true,
muteAudio = false,
muteVideo = false;
bool remoteUserMicMute = false, remoteUserVideoMute = false;
bool resizeVideo = false;
ServerHandler _serverHandler = ServerHandler();
String? token;
String? channelName;
String? channelId;
late user.Details userDetails;
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
getAgoraToken().then((value) {
if (value) {
initForAgora();
} else {
_showMyDialog();
}
});
// initForAgora();
}
Future<void> _showMyDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('No Channel Found'),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text('No video channel has been created by the Doctor'),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('Ok'),
onPressed: () async {
// int count = 0;
Navigator.pop(context);
Navigator.pop(_scaffoldKey.currentContext!);
},
),
],
);
},
);
}
Future<bool> getAgoraToken() async {
await Screen.keepOn(true);
var tokenBody = await _serverHandler.joinAgoraChannel(
widget.doctorId, widget.bookingId);
print('token Body from videoPage' + tokenBody.toString());
if (tokenBody['success'] == 1) {
setState(() {
token = tokenBody['channel']['access_token_patient'];
channelName = tokenBody['channel']['channel_name'];
print('**********Token Set********' + token!);
channelId = tokenBody['channel']['id'].toString();
});
return true;
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
"Unable to get Video Call Token, Please check your internet Connection and try again."),
));
return false;
}
}
Future<void> initForAgora() async {
// retrieve permissions
await [Permission.microphone, Permission.camera].request();
userDetails = await _serverHandler.getUserProfile();
//create the engine
_engine = await RtcEngine.create(appId);
await _engine?.enableVideo();
_engine?.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (String channel, int uid, int elapsed) async {
print("local user $uid joined");
// await _engine!.enableVideo();
},
userJoined: (int uid, int elapsed) {
print("remote user $uid joined");
setState(() {
_remoteUid = uid;
});
},
tokenPrivilegeWillExpire: (token) async {
await getAgoraToken();
await _engine?.renewToken(token);
},
userOffline: (int uid, UserOfflineReason reason) {
print("remote user $uid left channel");
// _engine!.enableVideo();
setState(() {
_remoteUid = null;
});
_userLeftTheCall();
},
userMuteVideo: (int uid, bool isMute) {
print('Audio Mutted');
setState(() {
remoteUserVideoMute = isMute;
});
},
userMuteAudio: (int uid, bool isMute) {
print('Audio Mutted');
setState(() {
remoteUserMicMute = isMute;
});
},
),
);
await getAgoraToken();
await _engine?.joinChannel(token, channelName!, null, userDetails.id!);
}
// Create UI with local view and remote view
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
key: _scaffoldKey,
body: SafeArea(
child: Stack(
children: [
Center(
child:
resizeVideo ? _renderLocalPreview() : _renderRemoteVideo(),
),
if (remoteUserVideoMute)
BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 10,
sigmaY: 10,
),
child: Padding(
padding: const EdgeInsets.only(bottom: 32),
child: Center(
child: Text(
'Doctor video Paused',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
),
),
if (remoteUserMicMute)
Padding(
padding: const EdgeInsets.only(top: 32),
child: Center(
child: Text(
'Doctor Mic Muted',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
),
Positioned(
top: 8,
left: 8,
child: InkWell(
//Uncomment it to unable resize
// onTap: () {
// setState(() {
// resizeVideo = !resizeVideo;
// });
// },
child: Container(
width: 100,
height: 120,
child: Stack(
children: [
ImageFiltered(
imageFilter: muteVideo
? ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: resizeVideo
? _renderRemoteVideo()
: _renderLocalPreview(), // Widget that is blurred
),
if (muteVideo)
Positioned(
top: 4,
left: 4,
child: Icon(
Icons.videocam_off_outlined,
color: Colors.white,
size: 32,
),
),
if (muteAudio)
Positioned(
bottom: 4,
right: 4,
child: Icon(
Icons.mic_off_outlined,
color: Colors.white,
size: 32,
),
),
],
),
),
),
),
Positioned(
bottom: 32,
right: 8,
left: 8,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
InkWell(
onTap: () async {
await Screen.keepOn(false);
await _engine?.leaveChannel();
await _serverHandler.leaveChannel(channelId!);
Navigator.pop(_scaffoldKey.currentContext!);
},
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.red),
child: Icon(
Icons.call_end,
color: Colors.white,
),
),
),
InkWell(
onTap: this._switchCamera,
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white),
child: Icon(
Icons.flip_camera_android_rounded,
color: Colors.black87,
),
),
),
InkWell(
onTap: this._onToggleMuteVideo,
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white),
child: Icon(
muteVideo
? Icons.videocam_off_outlined
: Icons.videocam_outlined,
color: Colors.black87,
),
),
),
InkWell(
onTap: this._onToggleMute,
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white),
child: Icon(
muteAudio
? Icons.mic_off_outlined
: Icons.mic_none_outlined,
color: Colors.black87,
),
),
),
],
),
)
],
),
),
),
);
}
// current user video
Widget _renderLocalPreview() {
return RtcLocalView.SurfaceView();
}
// remote user video
Widget _renderRemoteVideo() {
// return RtcRemoteView.SurfaceView(uid: 70);
if (_remoteUid != null) {
return RtcRemoteView.SurfaceView(uid: _remoteUid!);
} else {
return Text(
'Please wait, doctor is joining shortly',
style: TextStyle(
color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
);
}
}
_switchCamera() {
_engine?.switchCamera().then((value) {
setState(() {
switchCamera = !switchCamera;
});
}).catchError((err) {
log('switchCamera $err');
});
}
void _onToggleMute() {
setState(() {
muteAudio = !muteAudio;
});
_engine!.muteLocalAudioStream(muteAudio);
}
void _onToggleMuteVideo() {
setState(() {
muteVideo = !muteVideo;
});
_engine!.muteLocalVideoStream(muteVideo);
}
Future<bool> _onWillPop() async {
return (await showDialog(
context: _scaffoldKey.currentContext!,
builder: (context) => new AlertDialog(
title: new Text('Are you sure?'),
content: new Text('Do you want to exit exit Video Call?'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(_scaffoldKey.currentContext!),
child: new Text('No'),
),
TextButton(
onPressed: () async {
await Screen.keepOn(false);
await _serverHandler.leaveChannel(channelId!);
await _engine?.leaveChannel();
Navigator.pop(_scaffoldKey.currentContext!);
Navigator.pop(_scaffoldKey.currentContext!);
},
child: new Text('Yes'),
),
],
),
)) ??
false;
}
_userLeftTheCall() async {
return (await showDialog(
context: _scaffoldKey.currentContext!,
builder: (context) => new AlertDialog(
title: new Text('Doctor Left'),
content: new Text('Doctor left this call please join Again'),
actions: <Widget>[
TextButton(
onPressed: () async {
// await _serverHandler.leaveChannel(channelId!);
await Screen.keepOn(false);
await _engine?.leaveChannel();
Navigator.pop(_scaffoldKey.currentContext!);
Navigator.pop(_scaffoldKey.currentContext!);
// Navigator.of(context).pop(true);
},
child: new Text('Okay'),
),
],
),
)) ?? false;
}
}
Please ignore things which are not relevent. Also the log is too long to share, the log keeps on running during the entire video call.
Mobile Screenshot
Thank You
Can anyone please tell me how do I select multiple options in checkboxlisttile.
Here I am able to click only one option. I want to set the status column in note table in database as completed when i check the particular item.
(Actually I want to select the item as completed and display it under another tab called completed. checkboxlisttile is created dynamically i.e from database. When a new note is added it is displayed in this listview.)
note_info.dart //this is the screen where notes are displayed i.e listview
import 'dart:io';
import 'package:vers2cts/models/note_model.dart';
import 'package:vers2cts/models/customer_model.dart';
import 'package:vers2cts/services/db_service.dart';
import 'package:vers2cts/utils/db_helper.dart';
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'new_note.dart';
class Note_Info extends StatefulWidget{
final String appBarTitle;
final CustomerModel customer;
//Note_Info();
Note_Info(this. customer, this.appBarTitle);
#override
State<StatefulWidget> createState() {
//return Note_InfoState();
return Note_InfoState(this. customer,this.appBarTitle);
}
}
class Note_InfoState extends State<Note_Info> {
DBService dbService = DBService();
List<NoteModel> noteList;
int count = 0;
static final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
NoteModel note=NoteModel();
String appBarTitle;
CustomerModel customer=new CustomerModel();
Note_InfoState(this.customer, this.appBarTitle);
bool rememberMe = false;
DateTime _date = DateTime.now();
TextEditingController custfNameController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
updateListView();
if (noteList == null) {
noteList = List<NoteModel>();
updateListView();
}
TextStyle titleStyle = Theme.of(context).textTheme.subhead;
var height = MediaQuery.of(context).size.height;
var name=customer.first_name+" "+customer.last_name;
custfNameController.text = name;
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(
Icons.add,
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => NewNote(customer,note)));
},
)
],
),
body: Container(
child: Column(
children: <Widget>[
TextField(controller: custfNameController,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center),
Padding(
padding: const EdgeInsets.all(15.0),
child: Row(children: [
ImageProfile(customer.cust_photo),
Padding(
padding: const EdgeInsets.only(left: 30.0),
child: IconButton(
icon: Icon(
Icons.call,
color: Colors.green,
size: 45,
),
onPressed: () {
},
),
),
],),
),
SizedBox(
height: 50,
child: AppBar(
bottom: TabBar(
tabs: [
Tab(
text: "All",
),
Tab(
text: "Pending",
),
Tab(
text: "Cancelled",
),
Tab(
text: "Completed",
),
],
),
),
),
// create widgets for each tab bar here
Expanded(
child: TabBarView(
children: [
// first tab bar view widget
Container(
child: getNotecheckList()
),
// second tab bar view widget
Container(
),
Container(
child: Center(
child: Text(
'Cancelled',
),
),
),
Container(
child: Center(
child: Text(
'Completed',
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 55.0,
width: 200,
child: RaisedButton(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
color: Theme
.of(context)
.primaryColorDark,
textColor: Colors.white,
child: Text('Save', textScaleFactor: 1.5,),
onPressed: () {
setState(() {
//_reset();
});
},
),
),
),
]
),
)
));
}
Widget ImageProfile(String fileName) {
return Center(
child: CircleAvatar(
radius: 80.0,
backgroundImage: fileName == null
?AssetImage('images/person_icon.jpg')
:FileImage(File(customer.cust_photo))),
);
}
Future<void> updateListView() {
final Future<Database> dbFuture = DB.init();
dbFuture.then((database) {
int cid=customer.cust_id;
Future<List<NoteModel>> noteListFuture = dbService.getCustomerNotes(cid);
noteListFuture.then((noteList) {
setState(() {
this.noteList = noteList;
this.count = noteList.length;
});
});
});
}
int _isChecked=-1;
ListView getNotecheckList() {
return ListView.builder(
itemCount: count,
itemBuilder: (BuildContext context, int position) {
return Card(
color: Colors.white,
elevation: 2.0,
child: CheckboxListTile(
title: Text(this.noteList[position].note),
subtitle: Text(this.noteList[position].actn_on),
//secondary: const Icon(Icons.web),
value: position== _isChecked,
onChanged: (bool value) {
setState(() {
_isChecked = value?position:-1;
});
},
controlAffinity: ListTileControlAffinity.leading,
),
);
},
);
}
}
new_note.dart //this is where new note is added.
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:smooth_star_rating/smooth_star_rating.dart';
import 'package:intl/intl.dart';
import 'package:vers2cts/models/customer_model.dart';
import 'package:vers2cts/models/note_model.dart';
import 'package:vers2cts/services/db_service.dart';
import 'package:vers2cts/utils/form_helper.dart';
class NewNote extends StatefulWidget{
final NoteModel note;
final CustomerModel customer;
NewNote(this.customer,this. note);
//Dropdown
/*
final String label;
final Function(Color) onChanged;
final double height;
final double width;
NewNote.fordropdwn({
Key key,
this.onChanged,
this.height = 25,
this.width = 150,
this.label,
}) : super(key: key);*/
#override
State<StatefulWidget> createState() {
//return New_NoteState(this.customer);
return New_NoteState(this.customer,this.note);
}
}
class New_NoteState extends State<NewNote> with SingleTickerProviderStateMixin{
New_NoteState(this.customer,this.note);
NoteModel note=new NoteModel();
CustomerModel customer=new CustomerModel();
TextEditingController NoteController=TextEditingController();
TextEditingController custfNameController = TextEditingController();
DateTime _reminderDate = DateTime.now();
DBService dbService=new DBService();
SpeedDial _speedDial(){
return SpeedDial(
// child: Icon(Icons.add),
animatedIcon: AnimatedIcons.add_event,
animatedIconTheme: IconThemeData(size: 24.0),
backgroundColor: Colors.yellow,
curve: Curves.easeInCirc,
children: [
SpeedDialChild(
child: Icon(Icons.location_on,color: Colors.yellow,),
//backgroundColor: Theme.of(context).primaryColor,
label: 'Add Location',
//labelBackgroundColor:Theme.of(context).primaryColor,
),
SpeedDialChild(
child: Icon(Icons.keyboard_voice),
//backgroundColor: Colors.yellow,
label: 'Add voice',
//labelBackgroundColor: Colors.yellow
),
SpeedDialChild(
child: Icon(Icons.attachment_outlined,color :Colors.redAccent),
//backgroundColor:Theme.of(context).primaryColorLight,
label: 'Add File',
// labelBackgroundColor: Theme.of(context).primaryColorLight
),
SpeedDialChild(
child: Icon(Icons.image,color: Colors.lightBlue,),
//backgroundColor: Colors.yellow,
label: 'Add Image',
// labelBackgroundColor: Colors.yellow,
),
],
);
}
//for DropDownMenu
Color value=Colors.red;
final List<Color> colors = [
Colors.red,
Colors.blue,
Colors.green,
Colors.yellow,
Colors.pink,
Colors.purple,
Colors.brown,
];
//for Switch
bool isSwitched = false;
var textValue = 'Switch is OFF';
void toggleSwitch(bool value) {
if(isSwitched == false)
{
setState(() {
isSwitched = true;
note.rmnd_ind=1;
//this.note.remindOn = _reminderDate.toString();
});
}
else
{
setState(() {
isSwitched = false;
note.rmnd_ind=0;
});
}
}
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var name=customer.first_name+customer.last_name;
custfNameController.text = name;
return WillPopScope(
onWillPop: () {
// Write some code to control things, when user press Back navigation button in device navigationBar
moveToLastScreen();
},
child: Scaffold(
appBar:AppBar(),
body:ListView(
children: <Widget>[
SizedBox(
height: 2.0,
),
TextField(controller: custfNameController,
style: TextStyle(
fontSize: 20.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center),
Align(
alignment: Alignment.centerLeft,
child: Text("Add New",textAlign: TextAlign.left,
style: TextStyle(fontSize: 22,fontWeight: FontWeight.bold),),
),
SizedBox(
height: 2.0,
),
Divider(),
SizedBox(
height: 2.0,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: NoteController,
decoration: InputDecoration(
border: OutlineInputBorder(
borderSide: const BorderSide(width: 2.0),)),
keyboardType: TextInputType.multiline,
minLines: 5,//Normal textInputField will be displayed
maxLines: 5, // when user presses enter it will adapt to it
onChanged: (value) {
this.note.note = value;
},
),
),
TableCalendar(
selectedDayPredicate: (day) {
return isSameDay(_reminderDate, day);
},
onDaySelected: (selectedDay, focusedDay) {
setState(() {
String _reminderDate = DateFormat('dd-MM-yyyy').format(selectedDay);
note.actn_on=_reminderDate.toString();
});
},// Set initial date
focusedDay: DateTime.now(),
firstDay: DateTime.utc(2010, 10, 16),
lastDay: DateTime.utc(2030, 3, 14),),
SizedBox(
height: height*0.03,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(//mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Remind me",style: TextStyle(fontSize: 20),),
Padding(
padding: const EdgeInsets.only(left:80.0),
child: Container(
child: Switch(
onChanged: toggleSwitch,
value: isSwitched,
//activeColor: Colors.blue,
//activeTrackColor: Colors.yellow,
//inactiveThumbColor: Colors.redAccent,
//inactiveTrackColor: Colors.orange,
),
),
),
],),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(mainAxisAlignment: MainAxisAlignment.start,
children:<Widget>[
Text("Priority",style: TextStyle(fontSize: 20.0),),
Padding(
padding: const EdgeInsets.only(left:20.0),
child: Container(
child: SmoothStarRating(
size: height=50.0,
allowHalfRating: false,
onRated: (value) {
this.note.prty=value;
print("rating value -> $value");
},
),
),
)]),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Color",style: TextStyle(fontSize: 20),),
Padding(
padding: const EdgeInsets.only(left:80.0),
child: Container(
child: DropdownButton<Color>(
value: value,
//hint: Text(widget.label ?? ''),
onChanged: (color) {
setState(() => value = color);
//widget.onChanged(color);
},
items: colors.map((e) => DropdownMenuItem(
value: e,
child: Container(
// width: 60.0,
//height: 10.0,
width: 60.0,
// height: widget.height,
color: e,
),
),
)
.toList(),
),
),
),
],),
),
SizedBox(
height: height*0.08,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 55.0,
width: 200,
child: RaisedButton(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
color: Theme.of(context).primaryColorDark,
textColor: Colors.white,
child: Text('Save',textScaleFactor: 1.5,),
onPressed: (){
setState(() {
_save();
});
},
),
),
),
],
),
floatingActionButton:_speedDial(),
));
}
void moveToLastScreen() {
Navigator.pop(context, true);
}
void _save() async {
moveToLastScreen();
note.cust_id=customer.cust_id;
print(customer.cust_id);
print(note.cust_id);
int result;
if (note.note_id != null) { // Case 1: Update operation
result = await dbService.updateNote(note);
} else { // Case 2: Insert Operation
result = await dbService.insertNote(note);
}
if (result != 0) { // Success
FormHelper.showAlertDialog(context,'Status', 'Note Saved Successfully');
} else { // Failure
FormHelper.showAlertDialog(context,'Status', 'Problem Saving Note');
}
}
}
db_service.dart
import 'package:vers2cts/models/customer_model.dart';
import 'package:vers2cts/models/languages_model.dart';
import 'package:vers2cts/models/note_model.dart';
import 'package:vers2cts/models/user_model.dart';
import 'package:vers2cts/utils/db_helper.dart';
class DBService {
Future<int> insertNote(NoteModel note) async {
await DB.init();
var result = await DB.insert(NoteModel.table, note);
return result;
}
Future<List<NoteModel>> getCustomerNotes(int customer) async {
await DB.init();
var res = await DB.rawQuery("note WHERE cust_id = '$customer'");
int count = res.length;
List<NoteModel> notelist = List<NoteModel>();
// For loop to create a 'Note List' from a 'Map List'
for (int i = 0; i < count; i++) {
notelist.add(NoteModel.fromMap(res[i]));
}
return notelist;
}
}
note_model.dart
import 'model.dart';
class NoteModel extends Model {
static String table = 'note';
bool isSelected=false;
int note_id;
int cust_id;
String note;
String actn_on;
int rmnd_ind;
double prty;
String colr;
String sts;
int id;
String cre_date;
String cre_by;
String mod_date;
String mod_by;
int txn_id;
int delete_ind;
NoteModel({
this.note_id,
this.cust_id,
this.note,
this.actn_on,
this.rmnd_ind,
this.prty,
this.colr,
this.sts,
this.id,
this.cre_date,
this.cre_by,
this.mod_date,
this.mod_by,
this.txn_id,
this.delete_ind
});
static NoteModel fromMap(Map<String, dynamic> map) {
return NoteModel(
note_id: map["note_id"],
cust_id: map['cust_id'],
note: map['note'].toString(),
actn_on: map['actn_on'].toString(),
rmnd_ind: map['rmnd_ind'],
prty: map['prty'],
colr: map['colr'].toString(),
sts: map['sts'].toString(),
id: map['id'],
cre_date: map['cre_date'].toString(),
cre_by: map['cre_by'].toString(),
mod_date: map['mod_date'].toString(),
mod_by: map['mod_by'].toString(),
txn_id: map['txn_id'],
delete_ind: map['delete_ind'],
);
}
Map<String, dynamic> toMap() {
Map<String, dynamic> map = {
'note_id': note_id,
'cust_id': cust_id,
'note':note,
'actn_on': actn_on,
'rmnd_ind': rmnd_ind,
'prty': prty,
'colr': colr,
'sts':sts,
'id': id,
'cre_date': cre_date,
'cre_by': cre_by,
'mod_date':mod_date,
'mod_by':mod_by,
'txn_id':txn_id,
'delete_ind': delete_ind
};
if (note_id != null) {
map['note_id'] = note_id;
}
return map;
}
}
db_helper.dart
import 'dart:async';
import 'package:vers2cts/models/model.dart';
import 'package:path/path.dart' as p;
import 'package:sqflite/sqflite.dart';
abstract class DB {
static Database _db;
static int get _version => 1;
static Future<Database> init() async {
if (_db != null) {
return _db;
}
try {
var databasesPath = await getDatabasesPath();
String _path = p.join(databasesPath, 'CTS.db');
_db = await openDatabase(_path, version: _version, onCreate: onCreate);
print('db location:'+_path);
} catch (ex) {
print(ex);
}
}
static void onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE note (note_id INTEGER PRIMARY KEY,cust_id INTEGER, '
'note TEXT, '
'actn_on TEXT, rmnd_ind INTEGER, prty REAL, colr TEXT,'
'sts TEXT,'
'id INTEGER, cre_date TEXT,cre_by TEXT, mod_date TEXT,mod_by TEXT, txn_id INTEGER, delete_ind INTEGER)');
}
static Future<List<Map<String, dynamic>>> query(String table) async =>
_db.query(table);
static Future<int> insert(String table, Model model) async =>
await _db.insert(table, model.toMap());
static Future<Batch> batch() async => _db.batch();
static Future<List<Map<String, dynamic>>> rawQuery(String table) async =>
_db.query(table);
}
You need to store what all values are selected from user and then play with it.
For example -
var selectedIndexes = [];
ListView getNotecheckList() {
return ListView.builder(
itemCount: count,
itemBuilder: (_, int index) {
return Card(
color: Colors.white,
elevation: 2.0,
child: CheckboxListTile(
title: Text(this.noteList[position].note),
subtitle: Text(this.noteList[position].actn_on),
value: selectedIndexes.contains(index),
onChanged: (_) {
if (selectedIndexes.contains(index)) {
selectedIndexes.remove(index); // unselect
} else {
selectedIndexes.add(index); // select
}
},
controlAffinity: ListTileControlAffinity.leading,
),
);
},
);
}
store only index or whole array and play around
Output :-
Code :-
import 'package:flutter/material.dart';
class CheckBoxExample extends StatefulWidget {
const CheckBoxExample({Key? key}) : super(key: key);
#override
State<CheckBoxExample> createState() => _CheckBoxExampleState();
}
class _CheckBoxExampleState extends State<CheckBoxExample> {
List multipleSelected = [];
List checkListItems = [
{
"id": 0,
"value": false,
"title": "Sunday",
},
{
"id": 1,
"value": false,
"title": "Monday",
},
{
"id": 2,
"value": false,
"title": "Tuesday",
},
{
"id": 3,
"value": false,
"title": "Wednesday",
},
{
"id": 4,
"value": false,
"title": "Thursday",
},
{
"id": 5,
"value": false,
"title": "Friday",
},
{
"id": 6,
"value": false,
"title": "Saturday",
},
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 64.0),
child: Column(
children: [
Column(
children: List.generate(
checkListItems.length,
(index) => CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
dense: true,
title: Text(
checkListItems[index]["title"],
style: const TextStyle(
fontSize: 16.0,
color: Colors.black,
),
),
value: checkListItems[index]["value"],
onChanged: (value) {
setState(() {
checkListItems[index]["value"] = value;
if (multipleSelected.contains(checkListItems[index])) {
multipleSelected.remove(checkListItems[index]);
} else {
multipleSelected.add(checkListItems[index]);
}
});
},
),
),
),
const SizedBox(height: 64.0),
Text(
multipleSelected.isEmpty ? "" : multipleSelected.toString(),
style: const TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}