flutter图片聊天泡泡_Flutter 气泡效果合集(全网最全)
【2020-1-10】
效果图
remind_bubble.gif
使用案例
class BubbleDemo extends StatefulWidget {
@override
State createState() {
return _BubbleDemoState();
}
}
class _BubbleDemoState extends State {
// 是否显示左边气泡
bool leftTipReplay = false;
// 是否显示左边气泡
bool rightTipReplay = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppbar.appBar(
context,
title: '提醒气泡组件',
theme: CustomAppbarTheme.white,
),
body: Column(
children: [
SizedBox(height: 50,),
// 气泡左边
GestureDetector(
child: Stack(
children: [
//主题
Container(
width: Screen.width,
height: 200,
color: Colors.red,
child: Center(
child: Text(
'BUBBLE_LEFT',
style: TextStyle(
fontSize: 36,
color: Colors.white,
),
),
),
),
Positioned(
top: 20,
left: -20,
child: _showLeftBubble(),
),
],
),
onTap: () {
rightTipReplay = false;
leftTipReplay = true;
setState(() {});
},
),
SizedBox(height: 50,),
// 气泡右边
GestureDetector(
child: Stack(
children: [
//主题
Container(
width: Screen.width,
height: 200,
color: Colors.blueAccent,
child: Center(
child: Text(
'BUBBLE_RIGHT',
style: TextStyle(
fontSize: 36,
color: Colors.white,
),
),
),
),
Positioned(
top: 20,
left: -20,
child: _showRightBubble(),
),
],
),
onTap: () {
leftTipReplay = false;
rightTipReplay = true;
setState(() {});
},
)
],
),
);
}
Widget _showLeftBubble() {
return leftTipReplay
? Container(
width: AdaptationUtils.setWidth(228.0),
height: AdaptationUtils.setWidth(50.0),
child: TipBubble(
content: '双方在倒计时结束前确认CP关系后即可查看对方联系方式',
tipReplay: leftTipReplay,
// tipDuration: 90000,
top: AdaptationUtils.px(-20.0),
left: AdaptationUtils.px(64.0),
hornDirction: 'left',
),
)
: Container();
}
Widget _showRightBubble() {
return rightTipReplay
? Container(
width: AdaptationUtils.setWidth(228.0),
height: AdaptationUtils.setWidth(50.0),
child: TipBubble(
content: '双方在倒计时结束前确认CP关系后即可查看对方联系方式',
tipReplay: rightTipReplay,
// tipDuration: 90000,
top: AdaptationUtils.px(-20.0),
left: AdaptationUtils.px(64.0),
hornDirction: 'right',
),
)
: Container();
}
}
先上效果图(聊天气泡)
效果图
1.BubbleWidget封装
通过系统的Canvas绘制
/// 气泡组件封装
///
/// created by hujintao
/// created at 2019-10-21
//
import 'dart:math';
import 'package:flutter/material.dart';
enum BubbleArrowDirection { top, bottom, right, left, topLeft }
class BubbleWidget extends StatelessWidget {
// 尖角位置
final position;
// 尖角高度
var arrHeight;
// 尖角角度
var arrAngle;
// 圆角半径
var radius;
// 宽度
final width;
// 高度
final height;
// 边距
double length;
// 颜色
Color color;
// 边框颜色
Color borderColor;
// 边框宽度
final strokeWidth;
// 填充样式
final style;
// 子 Widget
final child;
// 子 Widget 与起泡间距
var innerPadding;
BubbleWidget(
this.width,
this.height,
this.color,
this.position, {
Key key,
this.length = 1,
this.arrHeight = 12.0,
this.arrAngle = 60.0,
this.radius = 10.0,
this.strokeWidth = 4.0,
this.style = PaintingStyle.fill,
this.borderColor,
this.child,
this.innerPadding = 6.0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (style == PaintingStyle.stroke && borderColor == null) {
borderColor = color;
}
if (arrAngle < 0.0 || arrAngle >= 180.0) {
arrAngle = 60.0;
}
if (arrHeight < 0.0) {
arrHeight = 0.0;
}
if (radius < 0.0 || radius > width * 0.5 || radius > height * 0.5) {
radius = 0.0;
}
if (position == BubbleArrowDirection.top ||
position == BubbleArrowDirection.bottom) {
if (length < 0.0 || length >= width - 2 * radius) {
length = width * 0.5 - arrHeight * tan(_angle(arrAngle * 0.5)) - radius;
}
} else {
if (length < 0.0 || length >= height - 2 * radius) {
length =
height * 0.5 - arrHeight * tan(_angle(arrAngle * 0.5)) - radius;
}
}
if (innerPadding < 0.0 ||
innerPadding >= width * 0.5 ||
innerPadding >= height * 0.5) {
innerPadding = 2.0;
}
Widget bubbleWidget;
if (style == PaintingStyle.fill) {
bubbleWidget = Container(
width: width,
height: height,
child: Stack(children: [
CustomPaint(
painter: BubbleCanvas(context, width, height, color, position,
arrHeight, arrAngle, radius, strokeWidth, style, length)),
_paddingWidget()
]));
} else {
bubbleWidget = Container(
width: width,
height: height,
child: Stack(children: [
CustomPaint(
painter: BubbleCanvas(
context,
width,
height,
color,
position,
arrHeight,
arrAngle,
radius,
strokeWidth,
PaintingStyle.fill,
length)),
CustomPaint(
painter: BubbleCanvas(
context,
width,
height,
borderColor,
position,
arrHeight,
arrAngle,
radius,
strokeWidth,
style,
length)),
_paddingWidget()
]));
}
return bubbleWidget;
}
Widget _paddingWidget() {
return Padding(
padding: EdgeInsets.only(
top: (position == BubbleArrowDirection.top)
? arrHeight + innerPadding
: innerPadding,
right: (position == BubbleArrowDirection.right)
? arrHeight + innerPadding
: innerPadding,
bottom: (position == BubbleArrowDirection.bottom)
? arrHeight + innerPadding
: innerPadding,
left: (position == BubbleArrowDirection.left)
? arrHeight + innerPadding
: innerPadding),
child: Center(child: this.child));
}
}
class BubbleCanvas extends CustomPainter {
BuildContext context;
final position;
final arrHeight;
final arrAngle;
final radius;
final width;
final height;
final length;
final color;
final strokeWidth;
final style;
BubbleCanvas(
this.context,
this.width,
this.height,
this.color,
this.position,
this.arrHeight,
this.arrAngle,
this.radius,
this.strokeWidth,
this.style,
this.length);
@override
void paint(Canvas canvas, Size size) {
Path path = Path();
path.arcTo(
Rect.fromCircle(
center: Offset(
(position == BubbleArrowDirection.left)
? radius + arrHeight
: radius,
(position == BubbleArrowDirection.top)
? radius + arrHeight
: radius),
radius: radius),
pi,
pi * 0.5,
false);
if (position == BubbleArrowDirection.top) {
path.lineTo(length + radius, arrHeight);
path.lineTo(
length + radius + arrHeight * tan(_angle(arrAngle * 0.5)), 0.0);
path.lineTo(length + radius + arrHeight * tan(_angle(arrAngle * 0.5)) * 2,
arrHeight);
}
path.lineTo(
(position == BubbleArrowDirection.right)
? width - radius - arrHeight
: width - radius,
(position == BubbleArrowDirection.top) ? arrHeight : 0.0);
path.arcTo(
Rect.fromCircle(
center: Offset(
(position == BubbleArrowDirection.right)
? width - radius - arrHeight
: width - radius,
(position == BubbleArrowDirection.top)
? radius + arrHeight
: radius),
radius: radius),
-pi * 0.5,
pi * 0.5,
false);
if (position == BubbleArrowDirection.right) {
path.lineTo(width - arrHeight, length + radius);
path.lineTo(
width, length + radius + arrHeight * tan(_angle(arrAngle * 0.5)));
path.lineTo(width - arrHeight,
length + radius + arrHeight * tan(_angle(arrAngle * 0.5)) * 2);
}
path.lineTo(
(position == BubbleArrowDirection.right) ? width - arrHeight : width,
(position == BubbleArrowDirection.bottom)
? height - radius - arrHeight
: height - radius);
path.arcTo(
Rect.fromCircle(
center: Offset(
(position == BubbleArrowDirection.right)
? width - radius - arrHeight
: width - radius,
(position == BubbleArrowDirection.bottom)
? height - radius - arrHeight
: height - radius),
radius: radius),
pi * 0,
pi * 0.5,
false);
if (position == BubbleArrowDirection.bottom) {
path.lineTo(width - radius - length, height - arrHeight);
path.lineTo(
width - radius - length - arrHeight * tan(_angle(arrAngle * 0.5)),
height);
path.lineTo(
width - radius - length - arrHeight * tan(_angle(arrAngle * 0.5)) * 2,
height - arrHeight);
}
path.lineTo(
(position == BubbleArrowDirection.left) ? radius + arrHeight : radius,
(position == BubbleArrowDirection.bottom)
? height - arrHeight
: height);
path.arcTo(
Rect.fromCircle(
center: Offset(
(position == BubbleArrowDirection.left)
? radius + arrHeight
: radius,
(position == BubbleArrowDirection.bottom)
? height - radius - arrHeight
: height - radius),
radius: radius),
pi * 0.5,
pi * 0.5,
false);
if (position == BubbleArrowDirection.left) {
path.lineTo(arrHeight, height - radius - length);
path.lineTo(0.0,
height - radius - length - arrHeight * tan(_angle(arrAngle * 0.5)));
path.lineTo(
arrHeight,
height -
radius -
length -
arrHeight * tan(_angle(arrAngle * 0.5)) * 2);
}
path.lineTo((position == BubbleArrowDirection.left) ? arrHeight : 0.0,
(position == BubbleArrowDirection.top) ? radius + arrHeight : radius);
path.close();
canvas.drawPath(
path,
Paint()
..color = color
..style = style
..strokeCap = StrokeCap.round
..strokeWidth = strokeWidth);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
double _angle(angle) {
return angle * pi / 180;
}
2.气泡组件使用
注意事项
必填参数
宽度 ScreenUtil().setWidth(326),
高度 ScreenUtil().setWidth(64),
背景颜色 Color(0xff333333),
位置 BubbleArrowDirection.bottom
可选参数
箭头宽度 length: ScreenUtil().setWidth(20)
箭头高度 arrHeight : ScreenUtil().setWidth(12)
箭头读书 arrAngle: 75.0,
子Widget与起泡间距 innerPadding
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fpdxapp/components/bubble/bubble_widget.dart';
class BubblePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(children: [
SizedBox(
height: 20,
),
///1- 复制删除,撤回消息-气泡BottomRight
Padding(
padding: EdgeInsets.all(4.0),
child: BubbleWidget(
ScreenUtil().setWidth(326),
ScreenUtil().setWidth(64),
Color(0xff333333),
BubbleArrowDirection.bottom,
length: ScreenUtil().setWidth(20),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
// 复制按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'复制',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
// line
Container(
width: ScreenUtil().setWidth(1),
color: Color(0xff707070)),
// 删除按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'删除',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
// line
Container(
width: ScreenUtil().setWidth(1),
color: Color(0xff707070)),
// 撤回按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'撤回',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
],
),
arrHeight: ScreenUtil().setWidth(12),
arrAngle: 75.0,
innerPadding: 0.0,
)),
SizedBox(
height: 5,
),
///2- 复制删除,撤回消息-气泡BottomLeft
Padding(
padding: EdgeInsets.all(4.0),
child: BubbleWidget(
ScreenUtil().setWidth(326),
ScreenUtil().setWidth(64),
Color(0xff333333),
BubbleArrowDirection.bottom,
length: ScreenUtil().setWidth(250),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
// 复制按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'复制',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
// line
Container(
width: ScreenUtil().setWidth(1), color: Color(0xff707070)),
// 删除按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'删除',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
// line
Container(
width: ScreenUtil().setWidth(1), color: Color(0xff707070)),
// 撤回按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'撤回',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
],
),
arrHeight: ScreenUtil().setWidth(12),
arrAngle: 75.0,
innerPadding: 0.0,
),
),
SizedBox(
height: 5,
),
///3- 复制删除,撤回消息-气泡TopLeft
Padding(
padding: EdgeInsets.all(4.0),
child: BubbleWidget(
ScreenUtil().setWidth(326),
ScreenUtil().setWidth(64),
Color(0xff333333),
BubbleArrowDirection.top,
length: ScreenUtil().setWidth(20),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
// 复制按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'复制',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
// line
Container(
width: ScreenUtil().setWidth(1),
color: Color(0xff707070)),
// 删除按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'删除',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
// line
Container(
width: ScreenUtil().setWidth(1),
color: Color(0xff707070)),
// 撤回按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'撤回',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
],
),
arrHeight: ScreenUtil().setWidth(12),
arrAngle: 75.0,
innerPadding: 0.0,
)),
SizedBox(
height: 5,
),
///4- 复制删除,撤回消息-气泡TopRight
Padding(
padding: EdgeInsets.all(4.0),
child: BubbleWidget(
ScreenUtil().setWidth(326),
ScreenUtil().setWidth(64),
Color(0xff333333),
BubbleArrowDirection.top,
length: ScreenUtil().setWidth(250),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
// 复制按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'复制',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
// line
Container(
width: ScreenUtil().setWidth(1), color: Color(0xff707070)),
// 删除按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'删除',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
// line
Container(
width: ScreenUtil().setWidth(1), color: Color(0xff707070)),
// 撤回按钮
GestureDetector(
onTap: () {},
child: Container(
child: Center(
child: Text(
'撤回',
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
],
),
arrHeight: ScreenUtil().setWidth(12),
arrAngle: 75.0,
innerPadding: 0.0,
),
),
SizedBox(
height: 5,
),
// 气泡右
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerRight,
child: BubbleWidget(200.0, 40.0, Colors.blue.withOpacity(0.7),
BubbleArrowDirection.right,
child: Text('你好,我是BubbleWidget!',
style:
TextStyle(color: Colors.white, fontSize: 14.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.bottomLeft,
child: BubbleWidget(300.0, 40.0, Colors.red.withOpacity(0.7),
BubbleArrowDirection.top,
length: 20,
child: Text('你好,你有什么特性化?',
style:
TextStyle(color: Colors.white, fontSize: 14.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerRight,
child: BubbleWidget(300.0, 90.0, Colors.blue.withOpacity(0.7),
BubbleArrowDirection.right,
child: Text('我可以自定义:\n尖角方向,尖角高度,尖角角度,\n距圆角位置,圆角大小,边框样式等!',
style:
TextStyle(color: Colors.white, fontSize: 16.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerLeft,
child: BubbleWidget(140.0, 40.0, Colors.cyan.withOpacity(0.7),
BubbleArrowDirection.left,
child: Text('你有什么不足?',
style:
TextStyle(color: Colors.white, fontSize: 14.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerRight,
child: BubbleWidget(350.0, 60.0, Colors.green.withOpacity(0.7),
BubbleArrowDirection.right,
child: Text('我现在还不会动态计算高度,只可用作背景!',
style:
TextStyle(color: Colors.white, fontSize: 16.0))))),
Padding(
padding: EdgeInsets.all(4.0),
child: Container(
alignment: Alignment.centerLeft,
child: BubbleWidget(
105.0,
60.0,
Colors.deepOrange.withOpacity(0.7),
BubbleArrowDirection.left,
child: Text('继续加油!',
style:
TextStyle(color: Colors.white, fontSize: 16.0))))),
]),
appBar: AppBar(
centerTitle: true,
leading: GestureDetector(
child: Icon(Icons.arrow_back_ios,
size: 20, color: Color(0xff333333)),
onTap: () {
Navigator.of(context).maybePop();
},
),
title: Text(
'气泡合集',
style: TextStyle(color: Colors.black),
),
),
);
}
}
方案二:通过 自定义PopupRoute 实现BubbleWidget气泡
通过 自定义PopupRoute 实现多个Bubble只选择一个,避免多个
PopupWindow 自定义
通过路由实现气泡的显示和隐藏
通过 GlobalKey 获取Widget的位置
显示
Navigator.push(
context,
PopRoute(
child: Bubble()
),
);
效果图
效果图
关键源码封装
import 'package:flutter/material.dart';
class Popup extends StatelessWidget {
final Widget child;
final Function onClick; //点击child事件
final EdgeInsetsGeometry padding;
Popup({
@required this.child,
this.onClick,
this.padding,
});
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: GestureDetector(
child: Stack(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.transparent,
),
Padding(
child: child,
padding: padding,
),
],
),
onTap: () {
//点击空白处
Navigator.of(context).pop();
},
),
);
}
}
class PopRoute extends PopupRoute {
final Duration _duration = Duration(milliseconds: 300);
Widget child;
PopRoute({@required this.child});
@override
Color get barrierColor => null;
@override
bool get barrierDismissible => true;
@override
String get barrierLabel => null;
@override
Widget buildPage(BuildContext context, Animation animation,
Animation secondaryAnimation) {
return child;
}
@override
Duration get transitionDuration => _duration;
}
使用案例
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fpdxapp/components/bubble/bubble.dart';
import 'package:fpdxapp/components/bubble/bubble_widget.dart';
import 'package:fpdxapp/components/custom_appbar.dart';
import 'package:fpdxapp/constants/icons.dart';
import 'package:fpdxapp/utils/toast.dart';
import 'package:fpdxapp/utils/widget.dart';
class BubbleDemo extends StatefulWidget {
@override
State createState() {
return _BubbleDemoState();
}
}
class _BubbleDemoState extends State {
GlobalKey btnKey = GlobalKey(debugLabel: '1');
List menus = [{'text':'ShowMenu1','isYourSelf':true}, {'text':'ShowMenu2','isYourSelf':false}, {'text':'ShowMenu3','isYourSelf':true}];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppbar.appBar(
context,
title: '气泡',
theme: CustomAppbarTheme.white,
),
body: Container(
child: Column(
children: menus.map((item) {
return CellForRow(
text: item['text'],
isYourSelf: item['isYourSelf'],
);
}).toList(),
),
margin: EdgeInsets.only(top: 100),
),
);
}
}
class CellForRow extends StatefulWidget {
final String text;
final bool isYourSelf;
CellForRow({this.text,this.isYourSelf = true});
@override
_CellForRowState createState() => _CellForRowState();
}
class _CellForRowState extends State {
GlobalKey btnKey = GlobalKey();
@override
void initState() {
// TODO: implement initState
super.initState();
if (this.widget.text != null) {
btnKey = GlobalKey(debugLabel: this.widget.text);
}
}
@override
Widget build(BuildContext context) {
return Container(
child: MaterialButton(
key: btnKey,
height: 45.0,
onPressed: () {
_showBubble(
context,
globalKey: btnKey,
valueChanged: (i, text) {
Toast.show("值变化了${text}");
},
isYourSelf: this.widget.isYourSelf,
);
},
child: Text(this.widget.text),
),
);
}
///弹出退出按钮
void _showBubble(@required BuildContext context,
{Widget child,
GlobalKey globalKey,
List titles,
Function(int i, String text) valueChanged,
bool isYourSelf = true}) {
if (globalKey == null) {
return;
}
if (titles == null) {
titles = ['复制', '删除', '撤回'];
}
Rect rect = WidgetUtil.getRectWithWidgetGlobalKey(globalKey);
print('气泡===============>>>>>>>>>>>>rect${rect}');
Navigator.push(
context,
PopRoute(
child: isYourSelf
? Popup(
child: BubbleWidget(
ScreenUtil().setWidth(109) * titles.length,
ScreenUtil().setWidth(64),
Color(0xff333333),
BubbleArrowDirection.bottom,
length: ScreenUtil().setWidth(250),
child: Row(
mainAxisSize: MainAxisSize.max,
children: titles.asMap().keys.map((i) {
return Container(
child: Row(
children: [
GestureDetector(
onTap: () {
if (valueChanged != null) {
valueChanged(i, titles[i]);
}
Navigator.of(context).pop();
},
child: Container(
child: Center(
child: Text(
titles[i],
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
i == titles.length - 1
? Container()
: Container(
width: ScreenUtil().setWidth(1),
color: Color(0xff707070)),
],
),
);
}).toList(),
),
arrHeight: ScreenUtil().setWidth(12),
arrAngle: 75.0,
innerPadding: 0.0,
),
padding: EdgeInsets.fromLTRB(
rect.left + ScreenUtil().setWidth(40),
rect.top - ScreenUtil().setWidth(20),
rect.right,
rect.bottom),
)
: Popup(
child: BubbleWidget(
ScreenUtil().setWidth(109) * titles.length,
ScreenUtil().setWidth(64),
Color(0xff333333),
BubbleArrowDirection.bottom,
length: ScreenUtil().setWidth(20),
child: Row(
mainAxisSize: MainAxisSize.max,
children: titles.asMap().keys.map((i) {
return Container(
child: Row(
children: [
GestureDetector(
onTap: () {
if (valueChanged != null) {
valueChanged(i, titles[i]);
}
Navigator.of(context).pop();
},
child: Container(
child: Center(
child: Text(
titles[i],
style: TextStyle(
color: Color(0xffE4E4E4),
fontSize: ScreenUtil().setSp(20)),
),
),
width: ScreenUtil().setWidth(108),
height: ScreenUtil().setWidth(64),
),
),
i == titles.length - 1
? Container()
: Container(
width: ScreenUtil().setWidth(1),
color: Color(0xff707070)),
],
),
);
}).toList(),
),
arrHeight: ScreenUtil().setWidth(12),
arrAngle: 75.0,
innerPadding: 0.0,
),
padding: EdgeInsets.fromLTRB(
rect.left + ScreenUtil().setWidth(40),
rect.top - ScreenUtil().setWidth(20),
rect.right,
rect.bottom),
),
),
);
}
}
flutter图片聊天泡泡_Flutter 气泡效果合集(全网最全)相关推荐
- flutter图片聊天泡泡_Flutter 非常丰富的消息气泡效果合集
这个消息气泡可以用于社交的聊天气泡,或者其他长按弹出的效果, 应用场景挺多,主要是用于学习. 先上效果图 1.BubbleWidget封装 通过系统的Canvas绘制 /// 气泡组件封装 /// / ...
- flutter图片聊天泡泡_Flutter极致的业务封装——各类聊天气泡(一)
前言 真的有段时间没写博客了,因为过去的一段时间工作实在是太忙了
- flutter图片聊天泡泡_flutter即时聊天IM仿微信|flutter聊天界面
鉴于Flutter最近比较火,今天给大家分享的是基于flutter+dart全家桶技术开发的仿微信界面聊天FlutterChat项目实例.实现了消息+表情.图片预览.红包.长按菜单.视频/仿朋友圈等功 ...
- flutter图片聊天泡泡_Flutter/dart聊天实例|仿微信界面|红包|朋友圈
FlutterChatroom项目是基于flutter+dart+image_picker等技术实现的仿微信app聊天室实战项目. 一.技术框架编码/技术:Vscode + Flutter 1.12. ...
- flutter图片聊天泡泡_flutter/dart聊天实例|flutter仿微信
> Flutter 是 Google 开源的 UI 框架,能高效构建多平台精美应用. > [FlutterChat聊天室项目](https://juejin.im/post/5ebb5c4 ...
- reflect动画,Flutter Animations动画效果合集(全网最全)
CurvedAnimation(曲线动画) Tween(补间动画/伸缩动画) Loading加载动画(ProgressDialog) Fade 渐入淡出动画 AnimatedBuilder(曲线动画) ...
- CSS3常用动画效果合集(最全)
CSS3常见的动画效果合集: /* animation */ .a-bounce,.a-flip,.a-flash,.a-shake,.a-swing,.a-wobble,.a-ring{-webki ...
- flutter图片聊天泡泡_[Flutter]聊天气泡组件
import 'dart:math'; import 'package:flutter/material.dart'; // 尖角方向枚举 enum BubbleAngleDirection { le ...
- flutter图片聊天泡泡_基于 Flutter+Dart 聊天实例 | Flutter 仿微信界面聊天室
1.项目介绍 Flutter是目前比较流行的跨平台开发技术,凭借其出色的性能获得很多前端技术爱好者的关注,比如阿里闲鱼,美团,腾讯等大公司都有投入相关案例生产使用. flutter_chatroom项 ...
最新文章
- 纳微科技完成近亿元融资,华兴医疗产业基金独家投资
- Android JNI 传递对象
- 杭电2031进制转换
- AngularJS2 环境搭建:
- AUTOSAR从入门到精通100讲(三十五)-AUTOSAR BswM三部曲-概念实践代码分析
- Centos中查找文件、目录、内容
- wordpress教程:默认http头信息X-Pingback的隐藏与修改
- 联通辟谣“不支持华为 5G”;罗永浩称索尼手机不如锤子;Linux 5.2.1 发布 | 极客头条...
- XNOR.ai融资1200万美元
- mysql 存储过程 调度_mysql 存储过程和事件调度
- Serv-U权限提升再提升--记一次虚拟主机入侵
- 查看python的模块和函数帮助文档方法
- 神推荐:西瓜导航你值得拥有
- Kali系统学习:弱点扫描工具NMAP实战演示
- activiti工作流数据库表详细说明
- 如何识别图片文字?这几个识别图片文字软件简单又高效
- 关于火车采集文章发布到wordpress后台待审核模块的设置
- GO语言-第二节-顺序编程
- Yobili优碧俪打造轻奢营养食品,以品质破圈
- matlab用多个cpu,Matlab 多核 多个CPU 并行运算