import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'chats.dart'; import 'connect.dart'; import 'profile.dart'; import 'config.dart'; import 'dart:convert'; import 'dart:async'; import 'functions.dart'; import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart' as http; import 'main.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_ringtone_player/flutter_ringtone_player.dart'; import 'package:intl/intl.dart'; import 'package:location/location.dart'; import 'package:flutter_link_preview/flutter_link_preview.dart'; import 'package:maps_launcher/maps_launcher.dart'; import 'package:toast/toast.dart'; import 'package:image_picker/image_picker.dart'; class ChatMessage{ String message_content; String message_id; String message_type; String time; String type; String status; String id; ChatMessage({@required this.type,@required this.message_content, @required this.message_type, @required this.message_id, @required this.time, @required this.status, @required this.id}); factory ChatMessage.fromJson(Map json) { return ChatMessage( message_content: json['message'] as String, type: json['type'] as String, message_type: json['message_type'] as String, message_id: json['message_id'] as String, time: json['time'] as String, status: json['status'] as String, id: json['id'] as String, ); } } /// This Widget is the main application widget. class Chat extends StatelessWidget { String id; String userid; String online; String name; String image; int back; Chat({this.id,this.userid,this.online,this.name,this.image,this.back}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: MyAppStatefulWidget(id:id,userid:userid,online:online,name:name,image:image,back:back), ); } } class MyAppStatefulWidget extends StatefulWidget { String id; String userid; String online; String name; String image; int back; MyAppStatefulWidget({Key key,this.id,this.userid,this.online,this.name,this.image,this.back}) : super(key: key); @override _MyApp createState() => _MyApp(id:id,userid:userid,online:online,name:name,image:image,back:back); } class _MyApp extends State{ String id; String userid; String online; String name; String image; int back; String firebase_token = ""; _MyApp({this.id,this.userid,this.online,this.name,this.image,this.back}); List messages; List parseMessages(String responseBody) { final parsed = json.decode(responseBody).cast>(); return parsed.map((json) => ChatMessage.fromJson(json)).toList(); } Future get_firebase_token() async { SharedPreferences prefs = await SharedPreferences.getInstance(); firebase_token = prefs.getString('firebase_token') ?? ''; } final messageController = TextEditingController(); ScrollController _scrollController = new ScrollController(); String session_id = ""; int from = 0; StreamSubscription locationSubscription; Future init() async{ final prefs = await SharedPreferences.getInstance(); final session = prefs.getString('session') ?? ''; final fromid = prefs.getInt('userid') ?? 0; session_id = session; from = fromid; if(session_id.isNotEmpty){ chats(); } } void chats() async { //get suggestion function var res = await http.post( new Config().messages_url(session_id,userid)); print(userid); //in query there might be unwant character so, we encode the query to url if (res.statusCode == 200) { if (mounted) { setState(() { messages = parseMessages(res.body); jump(); print(messages); //update data value and UI }); } } } void delete_chat(String id) async { //get suggestion function var res = await http.post( new Config().delete_chat_url(session_id,id)); } void send_chat(String to,String message,String message_id) async { //get suggestion function var res = await http.post( new Config().message(firebase_token,session_id,to,message,message_id)); //in query there might be unwant character so, we encode the query to url if (res.statusCode == 200) { /* Later update in version 2 status of message */ } } void send(){ String message = messageController.text; if(message.isNotEmpty){ int index = messages.length; int now = index+1; String message_id = new Functions().generate_md5(from.toString()+"_"+userid+"_"+now.toString()+new Functions().timestamp().toString()); String status = "0"; DateTime dt = DateTime.now(); String time = DateFormat('dd/MM/yyyy kk:mm').format(dt); ChatMessage chat = new ChatMessage(type:"1",message_content:message,message_type: "sender",message_id: message_id,status:status,time:time,id: ""); setState(() { messageController.clear(); messages.add(chat); jump(); //Send message to the recipient send_chat(userid,message,message_id); }); } } bool sharing_location = false; BuildContext shareLocationDialogContext; shareLocationDialog(BuildContext context) { // set up the buttons Widget cancelButton = FlatButton( child: Padding( padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), //Button for generating QR code child:Text("Cancel",style: TextStyle(color: Colors.red,fontSize: 15),)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide(color: Colors.red), ), onPressed: () { Navigator.pop(shareLocationDialogContext); }, ); Widget continueButton = FlatButton( child: Padding( padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), //Button for generating QR code child:Text("Yes",style: TextStyle(color: Colors.green,fontSize: 15),)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide(color: Colors.green), ), onPressed: () { Navigator.pop(shareLocationDialogContext); share_location(); }, ); // set up the AlertDialog AlertDialog alert = AlertDialog( title: Text("Share Your Location", style: TextStyle( color: Colors.red, ),), content: Text("Are you sure you want to share your location with "+name+"?"), actions: [ cancelButton, continueButton, ], ); // show the dialog showDialog( context: context, builder: (BuildContext context) { shareLocationDialogContext = context; return alert; }, ); } void share_location() async{ setState(() { sharing_location = true; }); String latitude = "", longitude = ""; Location location = new Location(); bool _serviceEnabled; PermissionStatus _permissionGranted; _serviceEnabled = await location.serviceEnabled(); if (!_serviceEnabled) { _serviceEnabled = await location.requestService(); if (!_serviceEnabled) { return; } } _permissionGranted = await location.hasPermission(); if (_permissionGranted == PermissionStatus.denied) { _permissionGranted = await location.requestPermission(); if (_permissionGranted != PermissionStatus.granted) { return; } } location.changeSettings(accuracy:LocationAccuracy.high,distanceFilter: 0); await location.getLocation().then((res) { latitude = res.latitude.toString(); longitude = res.longitude.toString(); //Send to server send_location(latitude,longitude); }); } Future file; String base64Image; File tmpFile; final captionController = TextEditingController() ; void send_location(String latitude, String longitude) async{ if(latitude.isNotEmpty) { int index = messages.length; int now = index+1; String message = latitude+"_"+longitude; String message_id = new Functions().generate_md5(from.toString()+"_"+userid+"_"+now.toString()+new Functions().timestamp().toString()); String status = "0"; DateTime dt = DateTime.now(); String time = DateFormat('dd/MM/yyyy kk:mm').format(dt); ChatMessage chat = new ChatMessage(type:"3",message_content:message,message_type: "sender",message_id: message_id,status:status,time:time,id: ""); setState(() { messageController.clear(); messages.add(chat); jump(); //Send message to the recipient send_location_sharing(latitude,longitude,message_id); }); } } void send_location_sharing(String latitude, String longitude, String message_id) async{ if(latitude.isNotEmpty) { var res = await http.post( new Config().share_location_url(session_id,firebase_token,userid, latitude,longitude,message_id)); //in query there might be unwant character so, we encode the query to url if (res.statusCode == 200) { /* Update GUI that delivered */ } } } void pick_photo(setState){ setState(() { file = ImagePicker.pickImage(source: ImageSource.gallery); }); } void jump(){ WidgetsBinding.instance.addPostFrameCallback((_) { if(_scrollController.hasClients){ _scrollController.animateTo(_scrollController.position.maxScrollExtent+700, duration: Duration(milliseconds: 1), curve: Curves.easeInOut); double max = _scrollController.position.maxScrollExtent; print("You are here:$max"); } }); } @override initState() { init(); get_firebase_token(); super.initState(); jump(); final fbm = FirebaseMessaging(); fbm.requestNotificationPermissions(IosNotificationSettings( sound: true, badge: true, alert: true, )); fbm.configure( onMessage: (Map message) async { var action = message['data']['action']; print("Action:$action"); if(action=="1") { var toid = message['data']['toid']; var fromid = message['data']['fromid']; if(from==toid) { //If it is another person who sent the message, play sound if(userid !=fromid){ FlutterRingtonePlayer.playNotification(); } } //If it is the one being chatted with, create message object and add print("From:$from"); print("userid:$userid"); print("toid:$toid"); print("Fromid:$fromid"); if(((from==int.parse(toid))&&(int.parse(userid)==int.parse(fromid)))||((from==int.parse(fromid))&&(int.parse(userid)==int.parse(toid)))){ print("You are here"); var megs = message['data']['message']; var message_id = message['data']['message_id']; var type = message['data']['type']; var from_id = message['data']['from_id']; var to_id = message['data']['to_id']; var time = message['data']['time']; String id = ""; if(from==fromid){ id = from_id; }else{ id = to_id; } String message_type = ""; if(from==int.parse(fromid)){ message_type = "sender"; }else{ message_type = "receiver"; } ChatMessage chat = new ChatMessage(type:type,message_content:megs,message_type: message_type,message_id: message_id,status:"0",time:time,id:id); setState(() { messages.add(chat); jump(); }); } } }, onLaunch: (Map message) async { var action = message['data']['action']; print("Action:$action"); if(action=="1") { var toid = message['data']['toid']; var fromid = message['data']['fromid']; if(from==toid) { //If it is another person who sent the message, play sound if(userid !=fromid){ FlutterRingtonePlayer.playNotification(); } } //If it is the one being chatted with, create message object and add print("From:$from"); print("userid:$userid"); print("toid:$toid"); print("Fromid:$fromid"); if(((from==int.parse(toid))&&(int.parse(userid)==int.parse(fromid)))||((from==int.parse(fromid))&&(int.parse(userid)==int.parse(toid)))){ print("You are here"); var megs = message['data']['message']; var message_id = message['data']['message_id']; var type = message['data']['type']; var from_id = message['data']['from_id']; var to_id = message['data']['to_id']; var time = message['data']['time']; String id = ""; if(from==fromid){ id = from_id; }else{ id = to_id; } String message_type = ""; if(from==int.parse(fromid)){ message_type = "sender"; }else{ message_type = "receiver"; } ChatMessage chat = new ChatMessage(type:type,message_content:megs,message_type: message_type,message_id: message_id,status:"0",time:time,id:id); setState(() { messages.add(chat); jump(); }); } } }, onResume: (Map message) async { var action = message['data']['action']; print("Action:$action"); if(action=="1") { var toid = message['data']['toid']; var fromid = message['data']['fromid']; if(from==toid) { //If it is another person who sent the message, play sound if(userid !=fromid){ FlutterRingtonePlayer.playNotification(); } } //If it is the one being chatted with, create message object and add print("From:$from"); print("userid:$userid"); print("toid:$toid"); print("Fromid:$fromid"); if(((from==int.parse(toid))&&(int.parse(userid)==int.parse(fromid)))||((from==int.parse(fromid))&&(int.parse(userid)==int.parse(toid)))){ print("You are here"); var megs = message['data']['message']; var message_id = message['data']['message_id']; var type = message['data']['type']; var from_id = message['data']['from_id']; var to_id = message['data']['to_id']; var time = message['data']['time']; String id = ""; if(from==fromid){ id = from_id; }else{ id = to_id; } String message_type = ""; if(from==int.parse(fromid)){ message_type = "sender"; }else{ message_type = "receiver"; } ChatMessage chat = new ChatMessage(type:type,message_content:megs,message_type: message_type,message_id: message_id,status:"0",time:time,id:id); setState(() { messages.add(chat); jump(); }); } } }, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0, automaticallyImplyLeading: false, backgroundColor: Colors.white, flexibleSpace: SafeArea( child: Container( padding: EdgeInsets.only(right: 16), child: Row( children: [ IconButton( onPressed: (){ if(back==1) { Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => Chats(), )); }else if(back==2){ Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => Connect(), )); }else if(back==3){ Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => MyApp(), )); } }, icon: Icon(Icons.arrow_back,color: Colors.black,), ), SizedBox(width: 2,), CircleAvatar( backgroundImage: NetworkImage(image), maxRadius: 20, ), SizedBox(width: 12,), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text(name,style: TextStyle( fontSize: 16 ,fontWeight: FontWeight.w600),), SizedBox(height: 6,), Text(online,style: TextStyle(color: Colors.grey.shade600, fontSize: 13),), ], ), ), SizedBox(width: 2,), IconButton( onPressed: (){ Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => Profile(id:id,back:back), )); }, icon: Icon(Icons.account_circle,color: Colors.black,), ), ], ), ), ), ), body: Stack( children: [ Column( children: [ messages == null?Expanded( child:Container( padding: EdgeInsets.all(20), child: Center(child: CircularProgressIndicator()) //if is searching then show "Please wait" //else show search peopels text )):Expanded(child:ListView.builder( itemCount: messages.length, controller: _scrollController, shrinkWrap: true, padding: EdgeInsets.only(top: 10,bottom: 50), itemBuilder: (context, index){ final item = messages[index].id; return Dismissible( // Each Dismissible must contain a Key. Keys allow Flutter to // uniquely identify widgets. key: Key(item), // Provide a function that tells the app // what to do after an item has been swiped away. onDismissed: (direction) { // Remove the item from the data source. setState(() { messages.removeAt(index); }); }, confirmDismiss: (DismissDirection direction) async { return await showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text("Delete Confirmation", style: TextStyle( color: Colors.red, ),), content: const Text("Are you sure you want to delete this chat?"), actions: [ FlatButton( onPressed: () { Navigator.of(context).pop(true); delete_chat(messages[index].message_id); }, child: Padding( padding: const EdgeInsets.fromLTRB(20, 10, 20, 10), //Button for generating QR code child:Text("Delete",style: TextStyle(color: Colors.red,fontSize: 15),)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide(color: Colors.red), ), ), FlatButton( onPressed: () => Navigator.of(context).pop(false), child: Padding( padding: const EdgeInsets.fromLTRB(20, 10, 20, 10), //Button for generating QR code child:Text("Cancel",style: TextStyle(color: Colors.blue,fontSize: 15),)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), side: BorderSide(color: Colors.blue), ), ), ], ); }, ); }, background: Container( child: Padding( padding: const EdgeInsets.all(15), child: Row( children: [ Icon(Icons.delete, color: Colors.red), Text('Delete Chat', style: TextStyle(color: Colors.red)), ], ), ), ), child:Container( padding: EdgeInsets.only(left: 14,right: 14,top: 10,bottom: 10), child: Column( children: [ Align( alignment: (messages[index].message_type == "receiver"?Alignment.topLeft:Alignment.topRight), child: Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * .6), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: (messages[index].message_type == "receiver"?Colors.grey.shade200:Colors.blue[200]), ), padding: EdgeInsets.all(16), child:message_content(messages[index].type,messages[index].message_content), ) ), Align( alignment: (messages[index].message_type == "receiver"?Alignment.topLeft:Alignment.topRight), child: Container( constraints: BoxConstraints( maxWidth: 200), padding: EdgeInsets.all(16), child:messages[index].message_type == "receiver"?Text(messages[index].time, style: TextStyle(fontSize: 10,color: Colors.black38,),):message_status(messages[index].status,messages[index].time), ) ), ] ), ), ); }, )), Align( alignment: Alignment.bottomLeft, child: Container( padding: EdgeInsets.only(left: 10,bottom: 10,top: 10), height: 60, width: double.infinity, color: Colors.white, child: Row( children: [ GestureDetector( onTap: (){ showModalBottomSheet( shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20.0))), backgroundColor: Colors.white, isScrollControlled: true, context: context, builder: (context) { return Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: new Icon(Icons.photo, color: Colors.blue,), title: new Text('Photo'), onTap: () { Navigator.pop(context); showGeneralDialog( context: context, pageBuilder: (context, animation, secondaryAnimation) => Scaffold( backgroundColor: Colors.white, appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: Text("Send To "+name, style: TextStyle( color: Colors.black)), automaticallyImplyLeading: true, //`true` if you want Flutter to automatically add Back Button when needed, //or `false` if you want to force your own back button every where leading: IconButton( icon: Icon(Icons.arrow_back, color: Colors.black), //onPressed:() => Navigator.pop(context, false), onPressed: () { Navigator.pop(context); }, ), // backgroundColor: Colors.white, elevation: 0.0, ), body: Stack( children: [ file !=null?FutureBuilder( future: file, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done && null != snapshot.data) { tmpFile = snapshot.data; base64Image = base64Encode(snapshot.data.readAsBytesSync()); return Container( decoration: new BoxDecoration( image: new DecorationImage( image: new FileImage(snapshot.data), fit: BoxFit.cover, )), ); } else if (null != snapshot.error) { return const Text( 'Error Picking Image', textAlign: TextAlign.center, ); }else{ return SizedBox( height: 0, ); } }, ):SizedBox( height: 0, ), Center( child: SingleChildScrollView( padding: EdgeInsets.all(10.0), child:Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ file !=null?SizedBox( height: 20, ):SizedBox( height: 0, ), file !=null?TextField( controller: captionController, textCapitalization: TextCapitalization.sentences, keyboardType: TextInputType.multiline, textInputAction: TextInputAction.newline, minLines: 1, maxLines: null, decoration: InputDecoration( prefixIcon: Icon( Icons.photo, color: Colors.black, ), hintStyle: TextStyle(color: Colors.black), filled: true, fillColor: Colors.white, hintText: 'Add Caption', border: OutlineInputBorder(borderRadius: BorderRadius.circular(30.0)) ), ):SizedBox( height: 0, ), SizedBox( height: 20, ), Container( color:Colors.white, child:Row( children: [ FlatButton( child: SizedBox( /// Enter your fixed width here, 300.0 ist just an example width: 100.0, child: ListTile( dense:true, contentPadding: EdgeInsets.only(left: 10.0, right: 10.0), visualDensity: VisualDensity(horizontal: 0, vertical: -4), minLeadingWidth:20, leading: Icon(Icons.photo), title: Text("Photo"), ), ), onPressed: () { pick_photo(setState); }, ), Spacer(), file !=null?FlatButton( child: SizedBox( /// Enter your fixed width here, 300.0 ist just an example width: 100.0, child: ListTile( dense:true, contentPadding: EdgeInsets.only(left: 10.0, right: 10.0), visualDensity: VisualDensity(horizontal: 0, vertical: -4), minLeadingWidth:20, leading: Icon(Icons.send), title: Text("Send"), ), ), onPressed: () { setState(() { }); }, ):SizedBox( height: 0, ), ])), ])) )]),//Put your screen design here! ), ); setState(() { file = ImagePicker.pickImage(source: ImageSource.gallery); }); }, ), ListTile( leading: new Icon(Icons.location_on_rounded, color: Colors.blue,), title: new Text('Location'), onTap: () { Navigator.pop(context); shareLocationDialog(context); }, ), ], ); }); }, child: Container( height: 30, width: 30, decoration: BoxDecoration( color: Colors.lightBlue, borderRadius: BorderRadius.circular(30), ), child: Icon(Icons.add, color: Colors.white, size: 20, ), ), ), SizedBox(width: 15,), Expanded( child: TextField( controller: messageController, textCapitalization: TextCapitalization.sentences, keyboardType: TextInputType.multiline, textInputAction: TextInputAction.newline, minLines: 1, maxLines: null, decoration: InputDecoration( hintText: "Write message...", hintStyle: TextStyle(color: Colors.black54), border: InputBorder.none ), onTap: () { jump(); }, ), ), SizedBox(width: 15,), FloatingActionButton( onPressed: (){ send(); }, child: Icon(Icons.send,color: Colors.white,size: 18,), backgroundColor: Colors.blue, elevation: 0, ), ], ), ), ), ]), ], ), ); } Widget message_content(String type,String message){ if(type=="1"){ return Text(message, style: TextStyle(fontSize: 15),); }else if(type=="3"){ var coords = message.split("_"); String url = "https://www.google.com/maps/search/?api=1&query="+coords[0]+"%2C"+coords[1]; return FlatButton( child: FlutterLinkPreview(url: url), onPressed: () { MapsLauncher.launchCoordinates(double.parse(coords[0]), double.parse(coords[1])); }, ); } } Widget message_status(String status,String time){ print("Status$status"); if(status=="0"){ return ListTile( dense:true, contentPadding: EdgeInsets.only(left: 10.0, right: 10.0), visualDensity: VisualDensity(horizontal: 0, vertical: -4), minLeadingWidth:20, title: Text(time, style: TextStyle( color: Colors.black38, )), leading: Icon(Icons.check, color: Colors.black38), ); }else if(status=="1"){ return ListTile( dense:true, contentPadding: EdgeInsets.only(left: 10.0, right: 10.0), visualDensity: VisualDensity(horizontal: 0, vertical: -4), minLeadingWidth:20, title: Text(time, style: TextStyle( color: Colors.black38, )), leading: SizedBox(width:50,child:Row(children: [Icon(Icons.check, color: Colors.black38),Icon(Icons.check, color: Colors.black38)])), ); }else if(status=="2"){ return ListTile( dense:true, contentPadding: EdgeInsets.only(left: 10.0, right: 10.0), visualDensity: VisualDensity(horizontal: 0, vertical: -4), minLeadingWidth:20, title: Text(time, style: TextStyle( color: Colors.black38, )), leading: SizedBox(width:50,child:Row(children: [Icon(Icons.check, color: Colors.blue),Icon(Icons.check, color: Colors.blue)])), ); } } }