overall bug fixes + UI changes
This commit is contained in:
parent
2275b582b4
commit
4f5ba12f2d
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
@ -14,5 +14,6 @@ class ScrollBehaviorModified extends ScrollBehavior {
|
||||
case TargetPlatform.windows:
|
||||
return const ClampingScrollPhysics();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -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>{
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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');
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
318
packages/simplex_app/lib/views/profile/profile_view.dart
Normal file
318
packages/simplex_app/lib/views/profile/profile_view.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
),
|
||||
),
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user