HarmonyOS next之harmony_flutter 自定义toast
ohos端建立通道弹出系统层toast
export default class FlutterToastPlugin implements FlutterPlugin {
private channel: MethodChannel | null = null;
getUniqueClassName(): string {
return TAG;
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.setupChannel(binding.getBinaryMessenger());
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
this.teardownChannel();
}
private setupChannel(messenger: BinaryMessenger) {
this.channel = new MethodChannel(messenger, "PonnamKarthik/fluttertoast", StandardMethodCodec.INSTANCE);
let handler = new MethodCallHandlerImpl();
this.channel.setMethodCallHandler(handler);
}
private teardownChannel() {
this.channel?.setMethodCallHandler(null)
this.channel = null
}
}
接收flutter层传递过来的参数
export default class MethodCallHandlerImpl implements MethodCallHandler{
onMethodCall(call: MethodCall, result: MethodResult): void {
switch (call.method) {
case 'showToast': {
let msg: string = call.argument('msg');
let time: number = call.argument('time') * 1000;
let gravity: string = call.argument("gravity");
let bgcolor: number = call.argument("bgcolor");
let textcolor: number = call.argument("textcolor");
let textSize: number = call.argument("fontSize");
try {
promptAction.showToast({message: msg, duration: time});
} catch (e) {
Log.e(TAG, "Show toast err " + e);
}
result.success(true);
}
case 'cancel': {
result.success(true);
}
default: {
result.notImplemented();
}
}
}
}
flutter端代码
enum Toast {
LENGTH_SHORT,
LENGTH_LONG
}
enum ToastGravity { TOP, BOTTOM, CENTER, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CENTER_LEFT, CENTER_RIGHT, SNACKBAR, NONE }
class Fluttertoast {
static const MethodChannel _channel = const MethodChannel('PonnamKarthik/fluttertoast');
static Future<bool?> cancel() async {
bool? res = await _channel.invokeMethod("cancel");
return res;
}
static Future<bool?> showToast({
required String msg,
Toast? toastLength,
int timeInSecForIosWeb = 1,
double? fontSize,
ToastGravity? gravity,
Color? backgroundColor,
Color? textColor,
bool webShowClose = false,
webBgColor = "linear-gradient(to right, #00b09b, #96c93d)",
webPosition = "right",
}) async {
String toast = "short";
if (toastLength == Toast.LENGTH_LONG) {
toast = "long";
}
String gravityToast = "bottom";
if (gravity == ToastGravity.TOP) {
gravityToast = "top";
} else if (gravity == ToastGravity.CENTER) {
gravityToast = "center";
} else {
gravityToast = "bottom";
}
if (backgroundColor == null) {
backgroundColor = Colors.black;
}
if (textColor == null) {
textColor = Colors.white;
}
final Map<String, dynamic> params = <String, dynamic>{
'msg': msg,
'length': toast,
'time': timeInSecForIosWeb,
'gravity': gravityToast,
'bgcolor': backgroundColor.value,
'iosBgcolor': backgroundColor.value,
'textcolor': textColor.value,
'iosTextcolor': textColor.value,
'fontSize': fontSize,
'webShowClose': webShowClose,
'webBgColor': webBgColor,
'webPosition': webPosition
};
bool? res = await _channel.invokeMethod('showToast', params);
return res;
}
}
typedef PositionedToastBuilder = Widget Function(BuildContext context, Widget child);
class FToast {
BuildContext? context;
static final FToast _instance = FToast._internal();
factory FToast() {
return _instance;
}
FToast init(BuildContext context) {
_instance.context = context;
return _instance;
}
FToast._internal();
OverlayEntry? _entry;
List<_ToastEntry> _overlayQueue = [];
Timer? _timer;
Timer? _fadeTimer;
_showOverlay() {
if (_overlayQueue.length == 0) {
_entry = null;
return;
}
if (context == null) {
removeQueuedCustomToasts();
throw ("Error: Context is null, Please call init(context) before showing toast.");
}
var _overlay;
try {
_overlay = Overlay.of(context!);
} catch (err) {
removeQueuedCustomToasts();
throw ("""Error: Overlay is null.
Please don't use top of the widget tree context (such as Navigator or MaterialApp) or
create overlay manually in MaterialApp builder.
More information
- https://github.com/ponnamkarthik/FlutterToast/issues/393
- https://github.com/ponnamkarthik/FlutterToast/issues/234""");
}
if (_overlay == null) {
removeQueuedCustomToasts();
throw ("""Error: Overlay is null.
Please don't use top of the widget tree context (such as Navigator or MaterialApp) or
create overlay manually in MaterialApp builder.
More information
- https://github.com/ponnamkarthik/FlutterToast/issues/393
- https://github.com/ponnamkarthik/FlutterToast/issues/234""");
}
_ToastEntry _toastEntry = _overlayQueue.removeAt(0);
_entry = _toastEntry.entry;
_overlay.insert(_entry!);
_timer = Timer(_toastEntry.duration, () {
_fadeTimer = Timer(_toastEntry.fadeDuration, () {
removeCustomToast();
});
});
}
removeCustomToast() {
_timer?.cancel();
_fadeTimer?.cancel();
_timer = null;
_fadeTimer = null;
_entry?.remove();
_entry = null;
_showOverlay();
}
removeQueuedCustomToasts() {
_timer?.cancel();
_fadeTimer?.cancel();
_timer = null;
_fadeTimer = null;
_overlayQueue.clear();
_entry?.remove();
_entry = null;
}
void showToast({
required Widget child,
PositionedToastBuilder? positionedToastBuilder,
Duration toastDuration = const Duration(seconds: 2),
ToastGravity? gravity,
Duration fadeDuration = const Duration(milliseconds: 350),
bool ignorePointer = false,
bool isDismissable = false,
}) {
if (context == null) throw ("Error: Context is null, Please call init(context) before showing toast.");
Widget newChild = _ToastStateFul(
child,
toastDuration,
fadeDuration,
ignorePointer,
!isDismissable
? null
: () {
removeCustomToast();
});
if (gravity == ToastGravity.BOTTOM) {
if (MediaQuery.of(context!).viewInsets.bottom != 0) {
gravity = ToastGravity.CENTER;
}
}
OverlayEntry newEntry = OverlayEntry(builder: (context) {
if (positionedToastBuilder != null) return positionedToastBuilder(context, newChild);
return _getPostionWidgetBasedOnGravity(newChild, gravity);
});
_overlayQueue.add(_ToastEntry(entry: newEntry, duration: toastDuration, fadeDuration: fadeDuration));
if (_timer == null) _showOverlay();
}
_getPostionWidgetBasedOnGravity(Widget child, ToastGravity? gravity) {
switch (gravity) {
case ToastGravity.TOP:
return Positioned(top: 100.0, left: 24.0, right: 24.0, child: child);
case ToastGravity.TOP_LEFT:
return Positioned(top: 100.0, left: 24.0, child: child);
case ToastGravity.TOP_RIGHT:
return Positioned(top: 100.0, right: 24.0, child: child);
case ToastGravity.CENTER:
return Positioned(top: 50.0, bottom: 50.0, left: 24.0, right: 24.0, child: child);
case ToastGravity.CENTER_LEFT:
return Positioned(top: 50.0, bottom: 50.0, left: 24.0, child: child);
case ToastGravity.CENTER_RIGHT:
return Positioned(top: 50.0, bottom: 50.0, right: 24.0, child: child);
case ToastGravity.BOTTOM_LEFT:
return Positioned(bottom: 50.0, left: 24.0, child: child);
case ToastGravity.BOTTOM_RIGHT:
return Positioned(bottom: 50.0, right: 24.0, child: child);
case ToastGravity.SNACKBAR:
return Positioned(bottom: MediaQuery.of(context!).viewInsets.bottom, left: 0, right: 0, child: child);
case ToastGravity.NONE:
return Positioned.fill(child: child);
case ToastGravity.BOTTOM:
default:
return Positioned(bottom: 50.0, left: 24.0, right: 24.0, child: child);
}
}
}
TransitionBuilder FToastBuilder() {
return (context, child) {
return _FToastHolder(
child: child!,
);
};
}
class _FToastHolder extends StatelessWidget {
const _FToastHolder({Key? key, required this.child}) : super(key: key);
final Widget child;
@override
Widget build(BuildContext context) {
final Overlay overlay = Overlay(
initialEntries: <OverlayEntry>[
OverlayEntry(
builder: (BuildContext ctx) {
return child;
},
),
],
);
return Directionality(
textDirection: TextDirection.ltr,
child: overlay,
);
}
}
class _ToastEntry {
final OverlayEntry entry;
final Duration duration;
final Duration fadeDuration;
_ToastEntry({
required this.entry,
required this.duration,
required this.fadeDuration,
});
}
class _ToastStateFul extends StatefulWidget {
_ToastStateFul(this.child, this.duration, this.fadeDuration, this.ignorePointer, this.onDismiss, {Key? key}) : super(key: key);
final Widget child;
final Duration duration;
final Duration fadeDuration;
final bool ignorePointer;
final VoidCallback? onDismiss;
@override
ToastStateFulState createState() => ToastStateFulState();
}
class ToastStateFulState extends State<_ToastStateFul> with SingleTickerProviderStateMixin {
showIt() {
_animationController!.forward();
}
hideIt() {
_animationController!.reverse();
_timer?.cancel();
}
AnimationController? _animationController;
late Animation _fadeAnimation;
Timer? _timer;
@override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: widget.fadeDuration,
);
_fadeAnimation = CurvedAnimation(parent: _animationController!, curve: Curves.easeIn);
super.initState();
showIt();
_timer = Timer(widget.duration, () {
hideIt();
});
}
@override
void deactivate() {
_timer?.cancel();
_animationController!.stop();
super.deactivate();
}
@override
void dispose() {
_timer?.cancel();
_animationController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onDismiss == null ? null : () => widget.onDismiss!(),
behavior: HitTestBehavior.translucent,
child: IgnorePointer(
ignoring: widget.ignorePointer,
child: FadeTransition(
opacity: _fadeAnimation as Animation<double>,
child: Center(
child: Material(
color: Colors.transparent,
child: widget.child,
),
),
),
),
);
}
}
flutter example 引用
fToast = FToast();
fToast.init(context);
fToast.showToast(
child: toast,
gravity: ToastGravity.BOTTOM,
toastDuration: Duration(seconds: 2),
positionedToastBuilder: (context, child) {
return Positioned(
child: child,
top: 16.0,
left: 16.0,
);
});