overall bug fixes + UI changes

This commit is contained in:
Muhammad Hamza 2021-10-11 14:57:05 +05:00
parent 2275b582b4
commit 4f5ba12f2d
25 changed files with 746 additions and 445 deletions

View File

@ -6,7 +6,7 @@ class Animator extends StatefulWidget {
final Widget child;
final Duration time;
const Animator(this.child, this.time, {Key? key}) : super(key: key);
const Animator(this.child, this.time, {Key key}) : super(key: key);
@override
_AnimatorState createState() => _AnimatorState();
@ -14,9 +14,9 @@ class Animator extends StatefulWidget {
class _AnimatorState extends State<Animator>
with SingleTickerProviderStateMixin {
Timer? timer;
AnimationController? animationController;
Animation? animation;
Timer timer;
AnimationController animationController;
Animation animation;
@override
void initState() {
@ -24,27 +24,27 @@ class _AnimatorState extends State<Animator>
animationController = AnimationController(
duration: const Duration(milliseconds: 290), vsync: this);
animation =
CurvedAnimation(parent: animationController!, curve: Curves.easeInOut);
timer = Timer(widget.time, animationController!.forward);
CurvedAnimation(parent: animationController, curve: Curves.easeInOut);
timer = Timer(widget.time, animationController.forward);
}
@override
void dispose() {
animationController!.dispose();
animationController.dispose();
super.dispose();
timer!.cancel();
timer.cancel();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation!,
animation: animation,
child: widget.child,
builder: (BuildContext context, Widget? child) {
builder: (BuildContext context, Widget child) {
return Opacity(
opacity: animation!.value,
opacity: animation.value,
child: Transform.translate(
offset: Offset(0.0, (1 - animation!.value) * 20),
offset: Offset(0.0, (1 - animation.value) * 20),
child: child,
),
);
@ -53,11 +53,11 @@ class _AnimatorState extends State<Animator>
}
}
Timer? timer;
Timer timer;
Duration duration = const Duration();
Duration wait() {
if (timer == null || !timer!.isActive) {
if (timer == null || !timer.isActive) {
timer = Timer(const Duration(microseconds: 120), () {
duration = const Duration();
});
@ -67,12 +67,12 @@ Duration wait() {
}
class WidgetAnimator extends StatelessWidget {
final Widget? child;
final Widget child;
const WidgetAnimator({this.child, Key? key}) : super(key: key);
const WidgetAnimator({this.child, Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Animator(child!, wait());
return Animator(child, wait());
}
}

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
class EntranceFader extends StatefulWidget {
/// Child to be animated on entrance
final Widget? child;
final Widget child;
/// Delay after which the animation will start
final Duration delay;
@ -14,7 +14,7 @@ class EntranceFader extends StatefulWidget {
final Offset offset;
const EntranceFader({
Key? key,
Key key,
this.child,
this.delay = const Duration(milliseconds: 0),
this.duration = const Duration(milliseconds: 400),
@ -29,39 +29,39 @@ class EntranceFader extends StatefulWidget {
class EntranceFaderState extends State<EntranceFader>
with SingleTickerProviderStateMixin {
AnimationController? _controller;
Animation? _dxAnimation;
Animation? _dyAnimation;
AnimationController _controller;
Animation _dxAnimation;
Animation _dyAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: widget.duration);
_dxAnimation =
Tween(begin: widget.offset.dx, end: 0.0).animate(_controller!);
Tween(begin: widget.offset.dx, end: 0.0).animate(_controller);
_dyAnimation =
Tween(begin: widget.offset.dy, end: 0.0).animate(_controller!);
Tween(begin: widget.offset.dy, end: 0.0).animate(_controller);
Future.delayed(widget.delay, () {
if (mounted) {
_controller!.forward();
_controller.forward();
}
});
}
@override
void dispose() {
_controller!.dispose();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller!,
animation: _controller,
builder: (context, child) => Opacity(
opacity: _controller!.value,
opacity: _controller.value,
child: Transform.translate(
offset: Offset(_dxAnimation!.value, _dyAnimation!.value),
offset: Offset(_dxAnimation.value, _dyAnimation.value),
child: widget.child,
),
),

View File

@ -14,5 +14,6 @@ class ScrollBehaviorModified extends ScrollBehavior {
case TargetPlatform.windows:
return const ClampingScrollPhysics();
}
return null;
}
}

View File

@ -17,7 +17,7 @@ void main() {
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
const MyApp({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -38,9 +38,7 @@ class MyApp extends StatelessWidget {
),
builder: (context, widget) {
return ScrollConfiguration(
behavior: const ScrollBehaviorModified(),
child: widget!,
);
behavior: const ScrollBehaviorModified(), child: widget);
},
initialRoute: AppRoutes.splash,
routes: <String, WidgetBuilder>{

View File

@ -1,9 +1,9 @@
import 'dart:convert';
class Contact {
final String? name;
final String? msg;
final String? msgTime;
final String name;
final String msg;
final String msgTime;
Contact({this.name, this.msg, this.msgTime});
@ -26,8 +26,8 @@ class Contact {
.toList(),
);
static List<Contact> decode(String? contacts) =>
(json.decode(contacts!) as List<dynamic>)
static List<Contact> decode(String contacts) =>
(json.decode(contacts) as List<dynamic>)
.map<Contact>((item) => Contact.fromJson(item))
.toList();
}

View File

@ -1,14 +1,20 @@
import 'dart:convert';
class Group {
final String? groupName;
final String? groupDescription;
final String photoPath;
final String groupName;
final String groupDescription;
final List<dynamic> members;
Group({this.groupName, this.groupDescription, this.members = const []});
Group(
{this.groupName,
this.photoPath,
this.groupDescription,
this.members = const []});
factory Group.fromJson(Map<String, dynamic> json) {
return Group(
photoPath: json['gPhoto'],
groupName: json['gName'],
groupDescription: json['desc'],
members: json['contacts'],
@ -17,6 +23,7 @@ class Group {
static Map<String, dynamic> toJson(Group group) {
return {
'gPhoto': group.photoPath,
'gName': group.groupName,
'desc': group.groupDescription,
'contacts': group.members,
@ -29,8 +36,8 @@ class Group {
.toList(),
);
static List<Group> decode(String? groups) =>
(json.decode(groups!) as List<dynamic>)
static List<Group> decode(String groups) =>
(json.decode(groups) as List<dynamic>)
.map<Group>((item) => Group.fromJson(item))
.toList();
}

View File

@ -1,7 +1,7 @@
import 'package:flutter/cupertino.dart';
class DrawerProvider extends ChangeNotifier {
int _currentIndex = 0;
int _currentIndex = 1;
int get currentIndex => _currentIndex;
@ -9,13 +9,4 @@ class DrawerProvider extends ChangeNotifier {
_currentIndex = value;
notifyListeners();
}
// toggle drawer
TickerFuture toggle(
AnimationController? animationController) {
return animationController!.isDismissed
? animationController.forward()
: animationController.reverse();
}
}

View File

@ -5,7 +5,7 @@ import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:simplex_chat/views/contacts/qr_code_details_view.dart';
class AddContactView extends StatefulWidget {
const AddContactView({Key? key}) : super(key: key);
const AddContactView({Key key}) : super(key: key);
@override
_AddContactViewState createState() => _AddContactViewState();
@ -13,16 +13,16 @@ class AddContactView extends StatefulWidget {
class _AddContactViewState extends State<AddContactView> {
final qrKey = GlobalKey(debugLabel: 'qr');
QRViewController? _qrViewController;
Barcode? result;
QRViewController _qrViewController;
Barcode result;
@override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
_qrViewController!.pauseCamera();
_qrViewController.pauseCamera();
} else if (Platform.isIOS) {
_qrViewController!.resumeCamera();
_qrViewController.resumeCamera();
}
}

View File

@ -9,14 +9,14 @@ import 'package:simplex_chat/model/contact.dart';
import 'package:simplex_chat/views/conversation/conversation_view.dart';
class ContactsView extends StatefulWidget {
const ContactsView({Key? key}) : super(key: key);
const ContactsView({Key key}) : super(key: key);
@override
_ContactsViewState createState() => _ContactsViewState();
}
class _ContactsViewState extends State<ContactsView> {
bool? _eraseMedia = false;
bool _eraseMedia = false;
List<Contact> _contactsList = []; // for storing contacts
@ -44,14 +44,16 @@ class _ContactsViewState extends State<ContactsView> {
// getting data from local storage FOR NOW!!
void _getContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _contacts = prefs.getString('contacts');
setState(() {
_contactsList = List.from(Contact.decode(_contacts));
});
final String _contacts = prefs.getString('contacts');
if (_contacts != null) {
setState(() {
_contactsList = List.from(Contact.decode(_contacts));
});
}
}
String? _photo;
String? _displayName;
String _photo = '';
String _displayName = '';
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -89,10 +91,13 @@ class _ContactsViewState extends State<ContactsView> {
],
),
const SizedBox(width: 10.0),
CircleAvatar(
backgroundImage: _photo == null
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_photo!)),
GestureDetector(
onTap: _addNewContacts,
child: CircleAvatar(
backgroundImage: _photo.isEmpty
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_photo)),
),
)
],
),
@ -138,10 +143,10 @@ class _ContactsViewState extends State<ContactsView> {
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text(_contactsList[index].name!),
subtitle: Text(_contactsList[index].msg!),
title: Text(_contactsList[index].name),
subtitle: Text(_contactsList[index].msg),
trailing: Text(
_contactsList[index].msgTime!,
_contactsList[index].msgTime,
style: const TextStyle(
fontSize: 11, color: Colors.grey),
),
@ -171,9 +176,9 @@ class _ContactsViewState extends State<ContactsView> {
offset: const Offset(-10, -120),
onSelected: (value) {
if (value == _options[0]) {
Navigator.pushNamed(context, AppRoutes.addContact);
} else {
Navigator.pushNamed(context, AppRoutes.scanInvitation);
} else {
Navigator.pushNamed(context, AppRoutes.addContact);
}
},
itemBuilder: (context) => _options
@ -325,7 +330,10 @@ class _ContactsViewState extends State<ContactsView> {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Contact> _localList = [];
_localList = List.from(Contact.decode(prefs.getString('contacts')));
final String _local = prefs.getString('contacts');
if (_local != null) {
_localList = List.from(Contact.decode(_local));
}
List<Contact> _newList = [
Contact(

View File

@ -4,9 +4,9 @@ import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/widgets/custom_btn.dart';
class QRCodeDetailsView extends StatelessWidget {
final Barcode? barcode;
final Barcode barcode;
const QRCodeDetailsView({
Key? key,
Key key,
this.barcode,
}) : super(key: key);
@ -26,7 +26,7 @@ class QRCodeDetailsView extends StatelessWidget {
),
const SizedBox(height: 30.0),
Text(
barcode!.code,
barcode.code,
style: kMediumHeadingStyle,
textAlign: TextAlign.center,
),

View File

@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:simplex_chat/widgets/message_bubble.dart';
class ConversationView extends StatefulWidget {
final String? name;
const ConversationView({Key? key, @required this.name}) : super(key: key);
final String name;
const ConversationView({Key key, @required this.name}) : super(key: key);
@override
_ConversationViewState createState() => _ConversationViewState();
@ -13,17 +13,17 @@ class _ConversationViewState extends State<ConversationView> {
final ScrollController _scrollController = ScrollController();
final TextEditingController _messageFieldController = TextEditingController();
FocusNode? _focus;
FocusNode _focus;
bool _fieldEnabled = false;
final List<Widget> _chatMessages = [];
@override
void initState() {
_focus = FocusNode();
_focus!.addListener(() {
debugPrint('FOCUS ${_focus!.hasFocus}');
_fieldEnabled = _focus!.hasFocus;
debugPrint('MESSAGE ENABLED! $_fieldEnabled');
_focus.addListener(() {
debugPrint('FOCUS ${_focus.hasFocus}');
_fieldEnabled = _focus.hasFocus;
debugPrint('MESSAGE ENABLED $_fieldEnabled');
});
super.initState();
}
@ -32,7 +32,7 @@ class _ConversationViewState extends State<ConversationView> {
void dispose() {
_messageFieldController.dispose();
_scrollController.dispose();
_focus!.dispose();
_focus.dispose();
super.dispose();
}
@ -42,7 +42,7 @@ class _ConversationViewState extends State<ConversationView> {
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(
title: Text('${widget.name}'),
title: Text(widget.name),
),
body: Column(
children: [
@ -50,7 +50,7 @@ class _ConversationViewState extends State<ConversationView> {
child: Container(
child: _chatMessages.isEmpty
? const Center(
child: Text('Send a message to get started!'),
child: Text('Send a message to get started'),
)
: SingleChildScrollView(
controller: _scrollController,
@ -96,7 +96,7 @@ class _ConversationViewState extends State<ConversationView> {
const SizedBox(width: 15.0),
InkWell(
onTap: () async {
if (_messageFieldController.text != '') {
if (_messageFieldController.text == '') {
setState(() {
_chatMessages.add(MessageBubble(
isUser: true,
@ -105,7 +105,7 @@ class _ConversationViewState extends State<ConversationView> {
));
});
_messageFieldController.clear();
_focus!.unfocus();
_focus.unfocus();
}
},
child: const Icon(Icons.send_rounded,

View File

@ -9,34 +9,61 @@ import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart';
class AddGroupView extends StatefulWidget {
const AddGroupView({Key? key}) : super(key: key);
const AddGroupView({Key key}) : super(key: key);
@override
_AddGroupViewState createState() => _AddGroupViewState();
}
class _AddGroupViewState extends State<AddGroupView> {
bool _addMember = false;
List<Contact> _contactsList = []; // for storing contacts
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File image;
String _groupPhotoPath = '';
bool _uploading = false;
bool _imageUploaded = false;
final List _members = [];
final _formKey = GlobalKey<FormState>();
final _displayNameController = TextEditingController();
final _descController = TextEditingController();
// getting data from local storage FOR NOW!!
bool _addMember = false;
List<Contact> _contactsList = []; // for storing contacts
// image buttons options
final _dpBtnText = ['Remove', 'Gallery', 'Camera'];
final _dpBtnColors = [Colors.red, Colors.purple, Colors.green];
final _dpBtnIcons = [
Icons.delete,
Icons.photo_rounded,
Icons.camera_alt_rounded
];
// getting data from local storage FOR NOW
void _getContacts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _contacts = prefs.getString('contacts');
final String _contacts = prefs.getString('contacts');
setState(() {
_contactsList = List.from(Contact.decode(_contacts));
});
}
String _userPhotoPath = '';
void _getUserPhoto() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String _photo = prefs.getString('photo${prefs.getString('displayName')}');
if (_photo != null) {
setState(() {
_userPhotoPath = _photo;
});
}
debugPrint(_userPhotoPath);
}
@override
void initState() {
_getUserPhoto();
_getContacts();
super.initState();
}
@ -68,8 +95,50 @@ class _AddGroupViewState extends State<AddGroupView> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 10.0),
const Center(
child: GroupDP(),
Center(
child: SizedBox(
height: 180.0,
width: 180.0,
child: Stack(
children: [
_imageUploaded
? CircleAvatar(
radius: 100.0,
backgroundImage:
FileImage(File(_groupPhotoPath)),
)
: const CircleAvatar(
radius: 100.0,
backgroundImage: AssetImage('assets/dp.png'),
),
Positioned(
right: 0,
bottom: 0,
child: FloatingActionButton(
backgroundColor: kSecondaryColor,
elevation: 2.0,
mini: true,
onPressed: _updateProfilePic,
child: _uploading
? const SizedBox(
height: 18.0,
width: 18.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white),
),
)
: const Icon(
Icons.add_a_photo,
size: 20,
),
),
)
],
),
),
),
const SizedBox(height: 25.0),
const Text('Group Name', style: kSmallHeadingStyle),
@ -79,8 +148,8 @@ class _AddGroupViewState extends State<AddGroupView> {
textInputType: TextInputType.name,
hintText: 'e.g College friends',
validatorFtn: (value) {
if (value!.isEmpty) {
return 'Group name cannot be empty!';
if (value.isEmpty) {
return 'Group name cannot be empty';
}
return null;
},
@ -148,10 +217,10 @@ class _AddGroupViewState extends State<AddGroupView> {
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
),
title: Text(_contactsList[index].name!),
title: Text(_contactsList[index].name),
onTap: () {
setState(() {
_members.add(_contactsList[index].name!);
_members.add(_contactsList[index].name);
});
},
),
@ -159,12 +228,14 @@ class _AddGroupViewState extends State<AddGroupView> {
)
: Container(),
const Divider(height: 30.0),
const ListTile(
ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
backgroundImage: _userPhotoPath == ''
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_userPhotoPath)),
),
title: Text('You'),
subtitle: Text(
title: const Text('You'),
subtitle: const Text(
'Owner',
style: TextStyle(color: Colors.grey, fontSize: 12.0),
)),
@ -178,9 +249,11 @@ class _AddGroupViewState extends State<AddGroupView> {
child: FloatingActionButton(
heroTag: 'setup',
onPressed: () async {
if (_formKey.currentState!.validate()) {
if (_formKey.currentState.validate()) {
FocusScope.of(context).unfocus();
_addNewGroup(_displayNameController.text.trim(),
_addNewGroup(
_groupPhotoPath,
_displayNameController.text.trim(),
_descController.text.trim());
_descController.clear();
_displayNameController.clear();
@ -194,102 +267,6 @@ class _AddGroupViewState extends State<AddGroupView> {
);
}
void _addNewGroup(String name, String desc) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = [];
_localList = List.from(Group.decode(prefs.getString('groups')));
List<Group> _groups = [
Group(
groupName: name,
groupDescription: desc,
members: _members,
),
];
_groups = _localList + _groups;
final String _newGroups = Group.encode(_groups);
await prefs.setString('groups', _newGroups);
var snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('$name added!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
}
class GroupDP extends StatefulWidget {
const GroupDP({Key? key}) : super(key: key);
@override
_GroupDPState createState() => _GroupDPState();
}
class _GroupDPState extends State<GroupDP> {
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File? image;
String photoUrl = '';
bool _uploading = false;
bool _imageUploaded = false;
// image buttons options
final _dpBtnText = ['Remove', 'Gallery', 'Camera'];
final _dpBtnColors = [Colors.red, Colors.purple, Colors.green];
final _dpBtnIcons = [
Icons.delete,
Icons.photo_rounded,
Icons.camera_alt_rounded
];
@override
Widget build(BuildContext context) {
return SizedBox(
height: 180.0,
width: 180.0,
child: Stack(
children: [
_imageUploaded
? CircleAvatar(
radius: 100.0,
backgroundImage: FileImage(image!),
)
: const CircleAvatar(
radius: 100.0,
backgroundImage: AssetImage('assets/dp.png'),
),
Positioned(
right: 0,
bottom: 0,
child: FloatingActionButton(
backgroundColor: kSecondaryColor,
elevation: 2.0,
mini: true,
onPressed: _updateProfilePic,
child: _uploading
? const SizedBox(
height: 18.0,
width: 18.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Icon(
Icons.add_a_photo,
size: 20,
),
),
)
],
),
);
}
void _updateProfilePic() {
showModalBottomSheet(
shape: const RoundedRectangleBorder(
@ -356,6 +333,7 @@ class _GroupDPState extends State<GroupDP> {
setState(() {
_imageUploaded = false;
image = null;
_groupPhotoPath = '';
});
Navigator.pop(context);
}
@ -376,6 +354,7 @@ class _GroupDPState extends State<GroupDP> {
setState(() {
_uploading = false;
_imageUploaded = true;
_groupPhotoPath = file.path;
});
} else {
setState(() {
@ -405,6 +384,7 @@ class _GroupDPState extends State<GroupDP> {
setState(() {
_uploading = false;
_imageUploaded = true;
_groupPhotoPath = file.path;
});
} else {
setState(() {
@ -417,4 +397,34 @@ class _GroupDPState extends State<GroupDP> {
rethrow;
}
}
void _addNewGroup(String photo, String name, String desc) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = [];
final String _local = prefs.getString('groups');
if (_local != null) {
_localList = List.from(Group.decode(_local));
}
List<Group> _groups = [
Group(
photoPath: _groupPhotoPath,
groupName: name,
groupDescription: desc,
members: _members,
),
];
_groups = _localList + _groups;
final String _newGroups = Group.encode(_groups);
await prefs.setString('groups', _newGroups);
var snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('$name added'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
}

View File

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/animations/bottom_animation.dart';
import 'package:simplex_chat/app_routes.dart';
@ -8,14 +9,14 @@ import 'package:simplex_chat/model/group.dart';
import 'package:simplex_chat/views/conversation/conversation_view.dart';
class GroupView extends StatefulWidget {
const GroupView({Key? key}) : super(key: key);
const GroupView({Key key}) : super(key: key);
@override
State<GroupView> createState() => _GroupViewState();
}
class _GroupViewState extends State<GroupView> {
bool? _eraseMedia = false;
bool _eraseMedia = false;
final List<String> _options = [
'Add group',
'Scan invitation',
@ -42,14 +43,28 @@ class _GroupViewState extends State<GroupView> {
// getting data from local storage FOR NOW!!
void _getGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? _groups = prefs.getString('groups');
final String _groups = prefs.getString('groups');
if (_groups != null) {
setState(() {
_groupList = List.from(Group.decode(_groups));
});
}
}
String _photo = '';
String _displayName = '';
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_groupList = List.from(Group.decode(_groups));
_displayName = prefs.getString('displayName');
_photo = prefs.getString('photo$_displayName');
});
}
@override
void initState() {
_getUserData();
_getGroups();
super.initState();
}
@ -62,15 +77,23 @@ class _GroupViewState extends State<GroupView> {
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
onTap: _addNewGroups,
child: SvgPicture.asset(
'assets/logo.svg',
height: 40.0,
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Hi! $_displayName', style: kSmallHeadingStyle),
const Text('Good day!'),
],
),
),
const SizedBox(width: 10.0),
CircleAvatar(
backgroundImage: _photo.isEmpty
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_photo)),
),
],
),
const SizedBox(height: 15.0),
Row(
@ -111,11 +134,14 @@ class _GroupViewState extends State<GroupView> {
_groupList.length,
(index) => WidgetAnimator(
child: ListTile(
leading: const CircleAvatar(
backgroundImage: AssetImage('assets/dp.png'),
leading: CircleAvatar(
backgroundImage: _groupList[index].photoPath == ''
? const AssetImage('assets/dp.png')
as ImageProvider
: FileImage(File(_groupList[index].photoPath)),
),
title: Text(_groupList[index].groupName!),
subtitle: Text(_groupList[index].groupDescription!),
title: Text(_groupList[index].groupName),
subtitle: Text(_groupList[index].groupDescription),
trailing: Text(
'Members: ${_groupList[index].members.length}',
style: const TextStyle(
@ -296,10 +322,14 @@ class _GroupViewState extends State<GroupView> {
}
// dummy ftn for loading new contacts
// ignore: unused_element
void _addNewGroups() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<Group> _localList = [];
_localList = List.from(Group.decode(prefs.getString('groups')));
final String _local = prefs.getString('groups');
if (_local != null) {
_localList = List.from(Group.decode(_local));
}
List<Group> _groups = [
Group(

View File

@ -7,19 +7,17 @@ import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/providers/drawer_providers.dart';
class MyDrawer extends StatelessWidget {
final AnimationController? animationController;
const MyDrawer({Key? key, this.animationController}) : super(key: key);
const MyDrawer({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
final _drawerProviders = Provider.of<DrawerProvider>(context);
return SizedBox(
width: MediaQuery.of(context).size.width * 0.82,
child: Material(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Builder(builder: (context) {
return Column(
children: [
const SizedBox(height: 30.0),
SvgPicture.asset(
@ -32,30 +30,22 @@ class MyDrawer extends StatelessWidget {
? kPrimaryColor
: Colors.transparent,
leading: Icon(
Icons.contact_phone,
Icons.person,
color: _drawerProviders.currentIndex == 0
? Colors.white
: Colors.grey,
),
title: Text(
'Your contacts',
'Your Profile',
style: TextStyle(
color: _drawerProviders.currentIndex == 0
? Colors.white
: Colors.black,
),
),
subtitle: Text(
'Start a conversation right away!',
style: TextStyle(
color: _drawerProviders.currentIndex == 0
? Colors.white
: Colors.grey,
),
),
onTap: () {
_drawerProviders.currentIndex = 0;
_drawerProviders.toggle(animationController);
Navigator.pop(context);
},
),
ListTile(
@ -63,30 +53,22 @@ class MyDrawer extends StatelessWidget {
? kPrimaryColor
: Colors.transparent,
leading: Icon(
Icons.insert_invitation,
Icons.contact_phone,
color: _drawerProviders.currentIndex == 1
? Colors.white
: Colors.grey,
),
title: Text(
'Invitations',
'Your contacts',
style: TextStyle(
color: _drawerProviders.currentIndex == 1
? Colors.white
: Colors.black,
),
),
subtitle: Text(
'Increase your contact circle!',
style: TextStyle(
color: _drawerProviders.currentIndex == 1
? Colors.white
: Colors.grey,
),
),
onTap: () {
_drawerProviders.currentIndex = 1;
_drawerProviders.toggle(animationController);
Navigator.pop(context);
},
),
ListTile(
@ -94,47 +76,62 @@ class MyDrawer extends StatelessWidget {
? kPrimaryColor
: Colors.transparent,
leading: Icon(
Icons.group,
Icons.insert_invitation,
color: _drawerProviders.currentIndex == 2
? Colors.white
: Colors.grey,
),
title: Text(
'Your groups',
'Invitations',
style: TextStyle(
color: _drawerProviders.currentIndex == 2
? Colors.white
: Colors.black,
),
),
subtitle: Text(
'Get in touch with numbers!',
onTap: () {
_drawerProviders.currentIndex = 2;
Navigator.pop(context);
},
),
ListTile(
tileColor: _drawerProviders.currentIndex == 3
? kPrimaryColor
: Colors.transparent,
leading: Icon(
Icons.group,
color: _drawerProviders.currentIndex == 3
? Colors.white
: Colors.grey,
),
title: Text(
'Your groups',
style: TextStyle(
color: _drawerProviders.currentIndex == 2
color: _drawerProviders.currentIndex == 3
? Colors.white
: Colors.grey,
: Colors.black,
),
),
onTap: () {
_drawerProviders.currentIndex = 2;
_drawerProviders.toggle(animationController);
_drawerProviders.currentIndex = 3;
Navigator.pop(context);
},
),
const Spacer(),
ListTile(
leading: const Icon(Icons.exit_to_app_rounded),
title: const Text('Logout'),
subtitle: const Text('Good bye! See you soon :)'),
onTap: () => _logout(context),
leading: const Icon(Icons.refresh),
title: const Text('Switch Profile'),
subtitle: const Text('*Not supported yet!*'),
onTap: () => _switchProfile(context),
),
],
),
),
);
}),
),
);
}
void _logout(BuildContext context) async {
void _switchProfile(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await Navigator.pushNamedAndRemoveUntil(
@ -142,7 +139,7 @@ class MyDrawer extends StatelessWidget {
AppRoutes.setupProfile,
(route) => route.settings.name == AppRoutes.setupProfile ? true : false,
);
String? _name = prefs.getString('displayName');
String _name = prefs.getString('displayName');
await prefs.remove('displayName');
await prefs.remove('fullName');
await prefs.remove('photo$_name');

View File

@ -1,4 +1,3 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -9,138 +8,60 @@ import 'package:simplex_chat/views/contacts/contacts_view.dart';
import 'package:simplex_chat/views/group/group_view.dart';
import 'package:simplex_chat/views/home/drawer.dart';
import 'package:simplex_chat/views/invitations/invitation_view.dart';
import 'package:simplex_chat/views/profile/profile_view.dart';
class HomeView extends StatefulWidget {
final double? maxSlide;
const HomeView({
Key? key,
this.maxSlide,
}) : super(key: key);
final double maxSlide;
const HomeView({Key key, this.maxSlide}) : super(key: key);
@override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> with TickerProviderStateMixin {
AnimationController? animationController;
bool? _canBeDragged;
class _HomeViewState extends State<HomeView> {
// views
final List<Widget> _views = [
const ProfileView(),
const ContactsView(),
const Invitations(),
const GroupView(),
];
@override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 250));
}
@override
Widget build(BuildContext context) {
final _drawerProviders = Provider.of<DrawerProvider>(context);
return WillPopScope(
onWillPop: _onWillPop,
child: GestureDetector(
onHorizontalDragStart: _onDragStart,
onHorizontalDragUpdate: _onDragUpdate,
onHorizontalDragEnd: _onDragEnd,
behavior: HitTestBehavior.translucent,
child: AnimatedBuilder(
animation: animationController!,
builder: (context, _) {
return Material(
color: Colors.white70,
child: SafeArea(
child: Stack(
children: [
Transform.translate(
offset: Offset(
widget.maxSlide! * (animationController!.value - 1),
0),
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(
math.pi / 2 * (1 - animationController!.value)),
alignment: Alignment.centerRight,
child: MyDrawer(
animationController: animationController,
),
),
child: SafeArea(
child: Scaffold(
drawer: const Drawer(
child: MyDrawer(),
),
body: Builder(builder: (context) {
return Stack(
children: [
_views[_drawerProviders.currentIndex],
Positioned(
top: MediaQuery.of(context).size.height * 0.03,
left: MediaQuery.of(context).size.width * 0.03,
child: InkWell(
onTap: () {
Scaffold.of(context).openDrawer();
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset('assets/menu.svg'),
),
Transform.translate(
offset: Offset(
widget.maxSlide! * animationController!.value, 0),
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(
-math.pi / 2 * animationController!.value),
alignment: Alignment.centerLeft,
child: _views[_drawerProviders.currentIndex]),
),
Positioned(
top: MediaQuery.of(context).padding.top,
left: MediaQuery.of(context).size.width * 0.03 +
animationController!.value * widget.maxSlide!,
child: InkWell(
onTap: () {
_drawerProviders.toggle(animationController);
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset(
'assets/menu.svg',
),
),
),
),
],
),
),
),
],
);
},
}),
),
),
);
}
void _onDragStart(DragStartDetails details) {
bool isDragOpenFromLeft = animationController!.isDismissed;
bool isDragCloseFromRight = animationController!.isCompleted;
_canBeDragged = isDragOpenFromLeft || isDragCloseFromRight;
}
void _onDragUpdate(DragUpdateDetails details) {
if (_canBeDragged!) {
double delta = details.primaryDelta! / widget.maxSlide!;
animationController!.value += delta;
}
}
void _onDragEnd(DragEndDetails details) {
double _kMinFlingVelocity = 365.0;
if (animationController!.isDismissed || animationController!.isCompleted) {
return;
}
if (details.velocity.pixelsPerSecond.dx.abs() >= _kMinFlingVelocity) {
double visualVelocity = details.velocity.pixelsPerSecond.dx /
MediaQuery.of(context).size.width;
animationController!.fling(velocity: visualVelocity);
} else if (animationController!.value < 0.5) {
animationController!.reverse();
} else {
animationController!.forward();
}
}
Future<bool> _onWillPop() async {
return (await showDialog(
context: context,

View File

@ -1,9 +1,33 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart';
class Invitations extends StatelessWidget {
const Invitations({Key? key}) : super(key: key);
class Invitations extends StatefulWidget {
const Invitations({Key key}) : super(key: key);
@override
State<Invitations> createState() => _InvitationsState();
}
class _InvitationsState extends State<Invitations> {
String _photo = '';
String _displayName = '';
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_displayName = prefs.getString('displayName');
_photo = prefs.getString('photo$_displayName');
});
}
@override
void initState() {
_getUserData();
super.initState();
}
@override
Widget build(BuildContext context) {
@ -13,12 +37,23 @@ class Invitations extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: SvgPicture.asset(
'assets/logo.svg',
height: 40.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Hi! $_displayName', style: kSmallHeadingStyle),
const Text('Good day!'),
],
),
const SizedBox(width: 10.0),
CircleAvatar(
backgroundImage: _photo.isEmpty
? const AssetImage('assets/dp.png') as ImageProvider
: FileImage(File(_photo)),
),
],
),
const SizedBox(height: 15.0),
Row(

View File

@ -4,7 +4,7 @@ import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart';
class IntroView extends StatelessWidget {
const IntroView({Key? key}) : super(key: key);
const IntroView({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View File

@ -0,0 +1,318 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart';
class ProfileView extends StatefulWidget {
const ProfileView({Key key}) : super(key: key);
@override
_ProfileViewState createState() => _ProfileViewState();
}
class _ProfileViewState extends State<ProfileView> {
final _formKey = GlobalKey<FormState>();
// controllers
final TextEditingController _displayNameController = TextEditingController();
final TextEditingController _fullNameController = TextEditingController();
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File image;
String _photo = '';
bool _uploading = false;
// image buttons options
final _dpBtnText = ['Remove', 'Gallery', 'Camera'];
final _dpBtnColors = [Colors.red, Colors.purple, Colors.green];
final _dpBtnIcons = [
Icons.delete,
Icons.photo_rounded,
Icons.camera_alt_rounded
];
String _displayName = '';
String _fullName = '';
void _getUserData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_fullName = prefs.getString('fullName');
_displayName = prefs.getString('displayName');
_photo = prefs.getString('photo$_displayName');
});
_displayNameController.text = _displayName;
if (_fullName != null) _fullNameController.text = _fullName;
}
@override
void initState() {
_getUserData();
super.initState();
}
@override
void dispose() {
_displayNameController.dispose();
_fullNameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 30),
Center(
child: SizedBox(
height: 180.0,
width: 180.0,
child: Stack(
children: [
_photo != ''
? CircleAvatar(
radius: 100.0,
backgroundImage: FileImage(File(_photo)),
)
: const CircleAvatar(
radius: 100.0,
backgroundImage: AssetImage('assets/dp.png'),
),
Positioned(
right: 0,
bottom: 0,
child: FloatingActionButton(
backgroundColor: kSecondaryColor,
elevation: 2.0,
mini: true,
onPressed: _updateProfilePic,
child: _uploading
? const SizedBox(
height: 18.0,
width: 18.0,
child: CircularProgressIndicator(
strokeWidth: 2.0,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white),
),
)
: const Icon(
Icons.add_a_photo,
size: 20,
),
),
)
],
),
)),
const SizedBox(height: 25.0),
const Text('Display Name', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
CustomTextField(
textEditingController: _displayNameController,
textInputType: TextInputType.name,
hintText: 'e.g John',
validatorFtn: (value) {
if (value.isEmpty) {
return 'Display name cannot be empty';
}
return null;
},
),
const SizedBox(height: 25.0),
const Text('Full Name', style: kSmallHeadingStyle),
const SizedBox(height: 10.0),
CustomTextField(
textEditingController: _fullNameController,
textInputType: TextInputType.name,
hintText: 'e.g John Doe',
),
const SizedBox(height: 25.0),
const Text(
'Your display name is what your contact will know you :)',
style: TextStyle(letterSpacing: 1.2),
)
],
),
),
),
),
),
),
floatingActionButton: Visibility(
visible: MediaQuery.of(context).viewInsets.bottom == 0,
child: FloatingActionButton(
heroTag: 'setup',
onPressed: () async {
if (_formKey.currentState.validate()) {
FocusScope.of(context).unfocus();
await _createProfile();
const snackBar = SnackBar(
backgroundColor: Colors.green,
content: Text('Profile updated!'),
);
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(snackBar);
}
},
child: const Icon(Icons.check),
),
),
);
}
// create profile and store in local
Future<void> _createProfile() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('displayName', _displayNameController.text.trim());
await prefs.setString('fullName', _fullNameController.text.trim());
await prefs.setString('photo${_displayNameController.text.trim()}', _photo);
debugPrint(prefs.getString('photo'));
}
void _updateProfilePic() {
showModalBottomSheet(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0),
),
),
context: context,
builder: (context) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(360.0)),
height: 7.0,
width: 50.0,
),
const SizedBox(height: 20.0),
const Align(
alignment: Alignment.centerLeft,
child: Text(
' Profile photo',
style: kHeadingStyle,
),
),
const SizedBox(height: 15.0),
Row(
children: List.generate(
3,
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
MaterialButton(
color: _dpBtnColors.map((e) => e).elementAt(index),
shape: const CircleBorder(),
onPressed: index == 0
? () => _removePic()
: index == 1
? () => _galleryPic()
: () => _cameraPic(),
child: Icon(
_dpBtnIcons.map((e) => e).elementAt(index),
color: Colors.white,
),
),
Text(
_dpBtnText.map((e) => e).elementAt(index),
textAlign: TextAlign.center,
)
],
),
))
],
),
),
);
}
void _removePic() {
setState(() {
image = null;
_photo = '';
});
Navigator.pop(context);
}
void _cameraPic() async {
try {
setState(() {
_uploading = true;
});
// picking Image from Camera
final file = await imgPicker.getImage(
source: ImageSource.camera,
);
if (file != null) {
image = File(file.path);
setState(() {
_uploading = false;
_photo = file.path;
});
} else {
setState(() {
_uploading = false;
});
}
Navigator.pop(context);
} catch (e) {
rethrow;
}
}
void _galleryPic() async {
try {
debugPrint('gallery pic');
setState(() {
_uploading = true;
});
// picking Image from local storage
final file = await imgPicker.getImage(
source: ImageSource.gallery,
);
if (file != null) {
image = File(file.path);
setState(() {
_uploading = false;
_photo = file.path;
});
} else {
setState(() {
_uploading = false;
});
}
Navigator.pop(context);
} catch (e) {
rethrow;
}
}
}

View File

@ -4,7 +4,7 @@ import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/widgets/custom_btn.dart';
class ScanInvitationView extends StatelessWidget {
const ScanInvitationView({Key? key}) : super(key: key);
const ScanInvitationView({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {

View File

@ -8,7 +8,7 @@ import 'package:simplex_chat/views/home/home_view.dart';
import 'package:simplex_chat/widgets/custom_text_field.dart';
class SetupProfileView extends StatefulWidget {
const SetupProfileView({Key? key}) : super(key: key);
const SetupProfileView({Key key}) : super(key: key);
@override
_SetupProfileViewState createState() => _SetupProfileViewState();
@ -22,7 +22,7 @@ class _SetupProfileViewState extends State<SetupProfileView> {
// Image Picker --> DP properties
final imgPicker = ImagePicker();
File? image;
File image;
String photoUrl = '';
bool _uploading = false;
bool _imageUploaded = false;
@ -63,7 +63,7 @@ class _SetupProfileViewState extends State<SetupProfileView> {
_imageUploaded
? CircleAvatar(
radius: 100.0,
backgroundImage: FileImage(image!),
backgroundImage: FileImage(image),
)
: const CircleAvatar(
radius: 100.0,
@ -105,8 +105,8 @@ class _SetupProfileViewState extends State<SetupProfileView> {
textInputType: TextInputType.name,
hintText: 'e.g John',
validatorFtn: (value) {
if (value!.isEmpty) {
return 'Display name cannot be empty!';
if (value.isEmpty) {
return 'Display name cannot be empty';
}
return null;
},
@ -118,12 +118,6 @@ class _SetupProfileViewState extends State<SetupProfileView> {
textEditingController: _fullNameController,
textInputType: TextInputType.name,
hintText: 'e.g John Doe',
validatorFtn: (value) {
if (value!.isEmpty) {
return 'Full name cannot be empty!';
}
return null;
},
),
const SizedBox(height: 25.0),
const Text(
@ -142,7 +136,7 @@ class _SetupProfileViewState extends State<SetupProfileView> {
child: FloatingActionButton(
heroTag: 'setup',
onPressed: () async {
if (_formKey.currentState!.validate()) {
if (_formKey.currentState.validate()) {
FocusScope.of(context).unfocus();
await _createProfile();
@ -269,6 +263,7 @@ class _SetupProfileViewState extends State<SetupProfileView> {
void _galleryPic() async {
try {
debugPrint('gallery pic');
setState(() {
_uploading = true;
});

View File

@ -1,14 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:progress_indicators/progress_indicators.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:simplex_chat/animations/entrance_fader.dart';
import 'package:simplex_chat/app_routes.dart';
import 'package:simplex_chat/constants.dart';
import 'package:simplex_chat/views/home/home_view.dart';
import 'package:simplex_chat/views/onBoarding/intro_view.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
const SplashScreen({Key key}) : super(key: key);
@override
_SplashScreenState createState() => _SplashScreenState();
@ -16,19 +14,11 @@ class SplashScreen extends StatefulWidget {
class _SplashScreenState extends State<SplashScreen> {
// logincheck
void _loginCheck() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? _name = prefs.getString('displayName');
void _loginCheck() {
Future.delayed(const Duration(seconds: 4), () {
Navigator.push(
Navigator.pushNamed(
context,
MaterialPageRoute(
builder: (_) => _name == null
? const IntroView()
: HomeView(
maxSlide: MediaQuery.of(context).size.width * 0.82,
),
),
AppRoutes.intro,
);
});
}

View File

@ -7,13 +7,13 @@ class CustomButton extends StatelessWidget {
@required this.onPressed,
@required this.color,
@required this.child,
Key? key})
Key key})
: super(key: key);
final double? width;
final double? height;
final void Function()? onPressed;
final Widget? child;
final Color? color;
final double width;
final double height;
final void Function() onPressed;
final Widget child;
final Color color;
@override
Widget build(BuildContext context) {

View File

@ -1,27 +1,27 @@
import 'package:flutter/material.dart';
class CustomTextField extends StatefulWidget {
final TextEditingController? textEditingController;
final TextInputType? textInputType;
final FocusNode? node;
final TextEditingController textEditingController;
final TextInputType textInputType;
final FocusNode node;
final String? hintText;
final bool? isPassword;
final IconData? icon;
final Color? iconColor;
final Color? passIconColor;
final String hintText;
final bool isPassword;
final IconData icon;
final Color iconColor;
final Color passIconColor;
final IconData? trailing;
final void Function()? trailingCallBack;
final IconData trailing;
final void Function() trailingCallBack;
final Function(String)? onChangeFtn;
final void Function()? onEditComplete;
final String? Function(String?)? validatorFtn;
final Function(String)? onFieldSubmit;
final String? errorText;
final Function(String) onChangeFtn;
final void Function() onEditComplete;
final String Function(String) validatorFtn;
final Function(String) onFieldSubmit;
final String errorText;
const CustomTextField({
Key? key,
Key key,
@required this.textEditingController,
@required this.textInputType,
this.trailing,

View File

@ -3,14 +3,14 @@ import 'package:simplex_chat/constants.dart';
class MessageBubble extends StatefulWidget {
const MessageBubble({
Key? key,
Key key,
this.sender,
this.text,
this.isUser,
}) : super(key: key);
final String? sender;
final String? text;
final bool? isUser;
final String sender;
final String text;
final bool isUser;
@override
_MessageBubbleState createState() => _MessageBubbleState();
@ -23,15 +23,15 @@ class _MessageBubbleState extends State<MessageBubble> {
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment:
widget.isUser! ? CrossAxisAlignment.end : CrossAxisAlignment.start,
widget.isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.sender!,
widget.sender,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
const SizedBox(height: 2.0),
Material(
borderRadius: widget.isUser!
borderRadius: widget.isUser
? const BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10),
@ -41,11 +41,11 @@ class _MessageBubbleState extends State<MessageBubble> {
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)),
elevation: 1.0,
color: widget.isUser! ? Colors.teal : kPrimaryColor,
color: widget.isUser ? Colors.teal : kPrimaryColor,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
child: Text(
widget.text!,
widget.text,
style: const TextStyle(fontSize: 15, color: Colors.white),
),
),

View File

@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.12.0 <3.0.0"
sdk: ">=2.10.0 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions