Last active
May 18, 2020 23:24
-
-
Save stegrams/47784372f62c3ab4dbb52c177e69c209 to your computer and use it in GitHub Desktop.
Chat sample that displays a dotted balloon as editing activity indicator.
This file contains 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'; | |
import 'package:flutter/scheduler.dart'; | |
import 'dart:async'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
home: Chat(), | |
); | |
} | |
} | |
class Chat extends StatefulWidget { | |
@override | |
ChatState createState() => ChatState(); | |
} | |
class ChatState extends State<Chat> { | |
Timer timer; | |
TextEditingController controller = TextEditingController(); | |
ScrollController scroller = ScrollController(); | |
@override | |
void dispose() { | |
scroller.dispose(); | |
super.dispose(); | |
} | |
void createTimer() { | |
timer?.cancel(); | |
timer = Timer.periodic( | |
Duration(seconds: 3), | |
(t) => setState(t.cancel), | |
); | |
} | |
List<Widget> bubbles = [ | |
BubbleMessage( | |
message: 'Hi there, this is a message', | |
time: '12:00', | |
delivered: true, | |
isMe: false, | |
), | |
BubbleMessage( | |
message: 'Whatsapp like bubble talk', | |
time: '12:01', | |
delivered: true, | |
isMe: false, | |
), | |
BubbleMessage( | |
message: 'Nice one, Flutter is awesome', | |
time: '12:00', | |
delivered: true, | |
isMe: true, | |
), | |
BubbleMessage( | |
message: 'I\'ve told you so dude!', | |
time: '12:00', | |
delivered: true, | |
isMe: false, | |
), | |
]; | |
@override | |
Widget build(BuildContext context) { | |
List<Widget> dottedMessage = []; | |
if (timer?.isActive ?? false) { | |
dottedMessage = [BubbleDotted()]; | |
} | |
// Scroll to the latest bubble when typing. | |
SchedulerBinding.instance.addPostFrameCallback((_) { | |
scroller.jumpTo(scroller.position.maxScrollExtent); | |
}); | |
return Scaffold( | |
backgroundColor: Colors.blueGrey.shade50, | |
appBar: AppBar( | |
backgroundColor: Colors.white, | |
elevation: .9, | |
title: Text( | |
'Chat', | |
style: TextStyle(color: Colors.green), | |
), | |
), | |
body: Column( | |
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
children: [ | |
Expanded( | |
child: ListView( | |
controller: scroller, | |
children: [...bubbles, ...dottedMessage], | |
), | |
), | |
TextField( | |
controller: controller, | |
onChanged: (String t) => setState(createTimer), | |
onSubmitted: (String t) => setState(() { | |
timer.cancel(); | |
if (t.isNotEmpty) | |
bubbles.add(BubbleMessage(message: t, delivered: true)); | |
controller.clear(); | |
}), | |
// Scroll to the latest bubble when keyboard pops out. | |
onTap: () => Timer( | |
Duration(milliseconds: 300), | |
() => scroller.jumpTo(scroller.position.maxScrollExtent), | |
), | |
decoration: InputDecoration( | |
hintText: 'Type a message and then Enter to send', | |
border: OutlineInputBorder(), | |
), | |
), | |
], | |
), | |
); | |
} | |
} | |
// Responsible for bubble's shape and color. | |
abstract class Bubble extends StatelessWidget { | |
Bubble({ | |
this.align = CrossAxisAlignment.start, | |
this.color = Colors.white, | |
}); | |
final CrossAxisAlignment align; | |
final Color color; | |
Widget body(BuildContext context); | |
@override | |
Widget build(BuildContext context) { | |
BorderRadius radius = BorderRadius.all(Radius.circular(5)); | |
CrossAxisAlignment direction; | |
if (align == CrossAxisAlignment.start) { | |
radius = radius.add( | |
BorderRadius.only( | |
bottomLeft: Radius.circular(10.0), | |
topLeft: Radius.circular(-5.0), | |
), | |
); | |
direction = align; | |
} else { | |
radius = radius.add( | |
BorderRadius.only( | |
bottomRight: Radius.circular(10.0), | |
topRight: Radius.circular(-5.0), | |
), | |
); | |
direction = CrossAxisAlignment.end; | |
} | |
return Column( | |
crossAxisAlignment: direction, | |
children: <Widget>[ | |
Container( | |
margin: const EdgeInsets.all(10), | |
padding: const EdgeInsets.all(8.0), | |
decoration: BoxDecoration( | |
boxShadow: [ | |
BoxShadow( | |
blurRadius: .5, | |
spreadRadius: 1.0, | |
color: Colors.black.withOpacity(.12)) | |
], | |
color: color, | |
borderRadius: radius, | |
), | |
child: body(context), | |
), | |
], | |
); | |
} | |
} | |
// Responsible for the message's layout | |
class BubbleMessage extends Bubble { | |
BubbleMessage({ | |
this.message, | |
this.time, | |
this.delivered = false, | |
bool isMe = true, | |
}) : super( | |
align: isMe ? CrossAxisAlignment.start : CrossAxisAlignment.end, | |
color: isMe ? Colors.white : Colors.greenAccent.shade100, | |
); | |
final String message, time; | |
final delivered; | |
@override | |
Widget body(BuildContext context) { | |
final icon = delivered ? Icons.done_all : Icons.done; | |
final now = time ?? | |
'${DateTime.now().hour}:'.padLeft(3, '0') + | |
'${DateTime.now().minute}'.padLeft(2, '0'); | |
return Stack( | |
children: <Widget>[ | |
Padding(padding: EdgeInsets.only(right: 48.0), child: Text(message)), | |
Positioned( | |
bottom: 0.0, | |
right: 0.0, | |
child: Row( | |
children: <Widget>[ | |
Text( | |
now, | |
style: TextStyle(color: Colors.black38, fontSize: 10.0), | |
), | |
SizedBox(width: 3.0), | |
Icon(icon, size: 12.0, color: Colors.black38), | |
], | |
), | |
) | |
], | |
); | |
} | |
} | |
// Simple animated dottes image inside a bubble. | |
class BubbleDotted extends Bubble { | |
BubbleDotted({ | |
bool isMe = true, | |
}) : super( | |
align: isMe ? CrossAxisAlignment.start : CrossAxisAlignment.end, | |
color: isMe ? Colors.white : Colors.greenAccent.shade100, | |
); | |
@override | |
Widget body(BuildContext context) { | |
return Image.network( | |
'https://oneecosystem.pensoft.net/i/simple_loading.gif', | |
width: 50, | |
height: 15, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment