Created
August 2, 2022 11:28
-
-
Save dpossas/18b0abeec7f9ef2213739e432d6c29c5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatefulWidget { | |
const MyApp({Key? key}) : super(key: key); | |
@override | |
State<MyApp> createState() => _MyAppState(); | |
} | |
class _MyAppState extends State<MyApp> { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
theme: ThemeData.light().copyWith( | |
scaffoldBackgroundColor: const Color(0xFFF8F9FA), | |
appBarTheme: const AppBarTheme( | |
backgroundColor: Color(0xFFFFFFFF), | |
foregroundColor: Color(0xFF212529), | |
elevation: 0, | |
centerTitle: false, | |
toolbarHeight: 92, | |
iconTheme: IconThemeData( | |
color: Color(0xFF212529), | |
size: 24, | |
), | |
), | |
inputDecorationTheme: const InputDecorationTheme( | |
border: OutlineInputBorder( | |
), | |
floatingLabelBehavior: FloatingLabelBehavior.always, | |
labelStyle: TextStyle( | |
fontSize: 16, | |
color: Color(0xFF212529), | |
), | |
hintStyle: TextStyle( | |
fontSize: 16, | |
color: Color(0xFFF8F9FA), | |
), | |
), | |
), | |
debugShowCheckedModeBanner: false, | |
home: const HomePage(), | |
); | |
} | |
} | |
/// view/chat/components/message_bubble.dart | |
class MessageBubble extends StatelessWidget { | |
final Message message; | |
final User activeUser; | |
const MessageBubble({ | |
Key? key, | |
required this.message, | |
required this.activeUser, | |
}); | |
@override | |
Widget build(BuildContext context) { | |
final theme = Theme.of(context); | |
return Padding( | |
padding: EdgeInsets.only( | |
right: message.isMine(activeUser) ? 0 : 90, | |
left: message.isMine(activeUser) ? 90 : 0, | |
), | |
child: Container( | |
padding: const EdgeInsets.symmetric( | |
horizontal: 15, | |
vertical: 10, | |
), | |
decoration: BoxDecoration( | |
color: message.isMine(activeUser) | |
? AppColors.backgroundBubbleColorOwner | |
: AppColors.backgroundBubbleColorOtherUser, | |
borderRadius: BorderRadius.only( | |
topLeft: Radius.circular(message.isMine(activeUser) ? 10 : 0), | |
topRight: Radius.circular(message.isMine(activeUser) ? 0 : 10), | |
bottomLeft: const Radius.circular(10), | |
bottomRight: const Radius.circular(10), | |
), | |
), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Text( | |
message.content, | |
style: TextStyle( | |
color: message.isMine(activeUser) | |
? AppColors.textColorBubbleColorOwner | |
: AppColors.textColorBubbleColorOtherUser, | |
), | |
), | |
const SizedBox(height: 8), | |
Text( | |
'2 mins ago', | |
style: theme.textTheme.caption?.copyWith( | |
color: AppColors.timeTextColor, | |
), | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
/// app_colors.dart | |
class AppColors { | |
static const Color backgroundBubbleColorOtherUser = Color(0xFFFFFFFF); | |
static const Color textColorBubbleColorOtherUser = Color(0xFF212529); | |
static const Color textColorBubbleColorOwner = Color(0xFFFFFFFF); | |
static const Color backgroundBubbleColorOwner = Color(0xFF212529); | |
static const Color timeTextColor = Color(0xFF9199A0); | |
} | |
/// models/user.dart | |
class User { | |
int id; | |
String name; | |
User({ | |
required this.id, | |
required this.name, | |
}); | |
} | |
/// models/message.dart | |
class Message { | |
String content; | |
User owner; | |
Message({ | |
required this.content, | |
required this.owner, | |
}); | |
bool isMine(User sender) => owner.id == sender.id; | |
} | |
/// enums/call_status.dart | |
enum CallStatus { inComming, inProgress, disconnected } | |
extension GetColorByCallStatus on CallStatus { | |
Color get backgroundColor { | |
switch (this) { | |
case CallStatus.disconnected: | |
return const Color(0xFFFF6B6B); | |
case CallStatus.inProgress: | |
default: | |
return const Color(0xFF20C997); | |
} | |
} | |
Color get textColor { | |
switch (this) { | |
case CallStatus.disconnected: | |
return const Color(0xFFE3F9F2); | |
case CallStatus.inProgress: | |
default: | |
return const Color(0xFFE3F9F2); | |
} | |
} | |
} | |
/// models/call.dart | |
class Call { | |
int durationInSeconds; | |
List<User> users; | |
List<Message> messages; | |
CallStatus status; | |
Call({ | |
this.durationInSeconds = 0, | |
this.users = const [], | |
this.messages = const [], | |
this.status = CallStatus.inComming, | |
}); | |
} | |
/// view/chat/components/call_status_bar.dart | |
class CallStatusBar extends StatefulWidget implements PreferredSizeWidget { | |
final String title; | |
final CallStatus callStatus; | |
const CallStatusBar({ | |
Key? key, | |
required this.title, | |
required this.callStatus, | |
}) : super(key: key); | |
@override | |
Size get preferredSize => const Size.fromHeight(35); | |
@override | |
State<CallStatusBar> createState() => _CallStatusBarState(); | |
} | |
class _CallStatusBarState extends State<CallStatusBar> { | |
@override | |
Widget build(BuildContext context) { | |
return Container( | |
decoration: BoxDecoration( | |
color: widget.callStatus.backgroundColor, | |
), | |
child: Center( | |
child: Text( | |
widget.title, | |
style: TextStyle( | |
color: widget.callStatus.textColor, | |
), | |
), | |
), | |
); | |
} | |
} | |
/// view/chat_page.dart | |
class ChatPage extends StatelessWidget { | |
ChatPage({Key? key}); | |
final userA = User(id: 1, name: 'Alice'); | |
final userB = User(id: 2, name: 'Douglas'); | |
@override | |
Widget build(BuildContext context) { | |
final call = Call( | |
status: CallStatus.inProgress, | |
users: [userA, userB], | |
messages: [ | |
Message( | |
owner: userA, | |
content: 'Hey! What\'s up?', | |
), | |
Message( | |
owner: userB, | |
content: 'Nothing much, wbu?', | |
), | |
Message( | |
owner: userA, | |
content: 'Hey! What\'s up?', | |
), | |
Message( | |
owner: userB, | |
content: 'Nothing much, wbu?', | |
) | |
], | |
); | |
return Scaffold( | |
bottomNavigationBar: Row( | |
mainAxisSize: MainAxisSize.max, | |
crossAxisAlignment: CrossAxisAlignment.center, | |
mainAxisAlignment: MainAxisAlignment.start, | |
children: <Widget>[ | |
Expanded( | |
child: Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: SizedBox( | |
height: 40, | |
child: TextFormField( | |
decoration: const InputDecoration( | |
filled: true, | |
enabled: true, | |
contentPadding: EdgeInsets.all(10), | |
focusColor: Colors.white, | |
fillColor: Colors.white, | |
hoverColor: Colors.white, | |
border: OutlineInputBorder( | |
borderRadius: BorderRadius.all(Radius.circular(50)), | |
borderSide: BorderSide.none, | |
), | |
suffixIcon: Icon(Icons.send_outlined), | |
hintText: 'Type something...', | |
), | |
), | |
), | |
), | |
), | |
const Padding( | |
padding: EdgeInsets.all(8.0), | |
child: Icon( | |
Icons.keyboard_voice, | |
color: Colors.white, | |
), | |
) | |
], | |
), | |
backgroundColor: const Color(0xFFF8F9FA), | |
appBar: const CallStatusBar( | |
title: 'Call In Progress With Alice - 00:30', | |
callStatus: CallStatus.inProgress, | |
), | |
body: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
AppBar( | |
elevation: 0, | |
backgroundColor: Colors.white, | |
leading: const Icon( | |
Icons.arrow_back, | |
color: Colors.black, | |
), | |
title: Text( | |
userA.name, | |
style: const TextStyle( | |
color: Colors.black, | |
), | |
), | |
centerTitle: false, | |
actions: const [ | |
Icon( | |
Icons.call, | |
color: Colors.black, | |
), | |
SizedBox(width: 20), | |
], | |
), | |
Expanded( | |
child: Container( | |
padding: const EdgeInsets.all(40), | |
child: ListView.separated( | |
itemCount: call.messages.length, | |
separatorBuilder: (_, index) => const SizedBox(height: 20), | |
itemBuilder: (_, index) => MessageBubble( | |
message: call.messages[index], | |
activeUser: userB, | |
), | |
), | |
), | |
), | |
], | |
), | |
); | |
} | |
} | |
/// view/home_page.dart | |
class HomePage extends StatelessWidget { | |
const HomePage({Key? key}); | |
@override | |
Widget build(BuildContext context) { | |
final theme = Theme.of(context); | |
return Scaffold( | |
body: Container( | |
padding: const EdgeInsets.fromLTRB(32, 140, 64, 32), | |
decoration: const BoxDecoration( | |
color: AppColors.textColorBubbleColorOtherUser, | |
), | |
child: Column( | |
children: [ | |
const SizedBox( | |
height: 140, | |
width: 140, | |
child: CircularProgressIndicator( | |
value: 1, | |
color: Color(0xFF18AFF6), | |
strokeWidth: 20.0, | |
), | |
), | |
const Expanded(child: SizedBox()), | |
DefaultHorizontalGradientButton( | |
width: double.maxFinite, | |
height: 50.0, | |
borderRadius: BorderRadius.circular(8), | |
child: Text( | |
'Chat', | |
style: theme.textTheme.bodyLarge?.copyWith( | |
color: Colors.white, | |
letterSpacing: 0, | |
), | |
), | |
), | |
const SizedBox(height: 16), | |
DefaultOutlinedButton( | |
width: double.maxFinite, | |
height: 50.0, | |
borderRadius: BorderRadius.circular(8), | |
child: Text( | |
'Settings', | |
style: theme.textTheme.bodyLarge?.copyWith( | |
color: Colors.white, | |
letterSpacing: 0, | |
), | |
), | |
) | |
], | |
), | |
), | |
); | |
} | |
} | |
/// view/settings_page.dart | |
class SettingsPage extends StatelessWidget { | |
const SettingsPage({Key? key}); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
leading: IconButton( | |
onPressed: () {}, | |
icon: const Icon(Icons.arrow_back), | |
), | |
title: const Text('Settings'), | |
), | |
body: Column( | |
children: const [ | |
TextField( | |
decoration: InputDecoration( | |
labelText: 'Full Name', | |
hintText: 'Insert your name...', | |
), | |
), | |
], | |
), | |
); | |
} | |
} | |
/// components/default_outlined_button.dart | |
class DefaultOutlinedButton extends StatefulWidget { | |
final Widget child; | |
final void Function()? onPressed; | |
final BorderRadiusGeometry? borderRadius; | |
final Gradient gradient; | |
final double height; | |
final double? width; | |
const DefaultOutlinedButton({ | |
Key? key, | |
required this.child, | |
this.onPressed, | |
this.borderRadius, | |
this.gradient = const LinearGradient(colors: [ | |
Color(0xFF18AFF6), | |
Color(0xFF5555FE), | |
]), | |
this.height = 50.0, | |
this.width, | |
}) : super(key: key); | |
@override | |
State<DefaultOutlinedButton> createState() => _DefaultOutlinedButtonState(); | |
} | |
class _DefaultOutlinedButtonState extends State<DefaultOutlinedButton> { | |
@override | |
Widget build(BuildContext context) { | |
final borderRadius = widget.borderRadius ?? BorderRadius.circular(20); | |
return SizedBox( | |
width: widget.width, | |
height: widget.height, | |
child: OutlinedButton( | |
onPressed: widget.onPressed, | |
style: OutlinedButton.styleFrom( | |
shape: RoundedRectangleBorder( | |
borderRadius: borderRadius, | |
), | |
side: const BorderSide(color: Colors.white, width: 1), | |
), | |
child: widget.child, | |
), | |
); | |
} | |
} | |
/// components/default_horizontal_gradient_button.dart | |
class DefaultHorizontalGradientButton extends StatefulWidget { | |
final Widget? child; | |
final void Function()? onPressed; | |
final BorderRadiusGeometry? borderRadius; | |
final Gradient gradient; | |
final double height; | |
final double? width; | |
const DefaultHorizontalGradientButton({ | |
Key? key, | |
this.child, | |
this.onPressed, | |
this.borderRadius, | |
this.gradient = const LinearGradient(colors: [ | |
Color(0xFF18B2FB), | |
Color(0xFF5555FE), | |
]), | |
this.height = 50.0, | |
this.width, | |
}) : super(key: key); | |
@override | |
State<DefaultHorizontalGradientButton> createState() => | |
_DefaultHorizontalGradientButtonState(); | |
} | |
class _DefaultHorizontalGradientButtonState | |
extends State<DefaultHorizontalGradientButton> { | |
@override | |
Widget build(BuildContext context) { | |
final borderRadius = widget.borderRadius ?? BorderRadius.circular(20); | |
return Container( | |
width: widget.width, | |
height: widget.height, | |
decoration: BoxDecoration( | |
gradient: widget.gradient, | |
borderRadius: borderRadius, | |
), | |
child: ElevatedButton( | |
onPressed: widget.onPressed, | |
style: ElevatedButton.styleFrom( | |
shape: RoundedRectangleBorder( | |
borderRadius: borderRadius, | |
), | |
), | |
child: widget.child, | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment