前言
在客户端开发过程中,时常要实现沉浸式状态栏,有时我们会遇到状态栏文字和背景图片颜色相近这样令人难受的情况,就会导致文字显示不清,像下面这样:
之前学 Android 的时候,看郭霖大神提到过这个问题 一个Android沉浸式状态栏上的黑科技,今天用 flutter 写的时候就想这个问题如何解决?
一、解决问题
经过一番查找,发现一个 “黑科技” statusbarz ,在 pubspec.yaml 加上它 ( statusbarz: ^1.0.11 ) 就行了。先看看结果:
而没使用之前是这样的:
二、使用步骤
statusbarz 的使用十分简单,将 StatusbarzCapturer 放在 MaterialApp 的上层并且将它实现的观测加入到 navigatorObservers 中去,像这样:
return StatusbarzCapturer(
child: MaterialApp(
debugShowCheckedModeBanner: false,
navigatorObservers: [Statusbarz.instance.observer],
routes: {
'/white': (context) => WhitePage(),
'/black': (context) => BlackPage(),
'/p1': (context) => ImagePage1(),
'/p2': (context) => ImagePage2(),
'/two': (context) => TwoColorPage(),
},
home: WhitePage(),
),
);
这时就可以实现开头看见的效果了。这里有一点要知道,statusbarz 在这种情况下,只会在页面变化的时候进行观测重绘。有一种手动改变状态栏颜色的办法:
Statusbarz.instance.refresh();
大家可以试试。
三、实现原理
现在来看看源码,看看它是如何实现的:
/// Finds currently rendered UI
RenderRepaintBoundary? boundary =
context.findRenderObject() as RenderRepaintBoundary?;
/// Converts rendered UI to png
var capturedImage = await boundary!.toImage(
pixelRatio: 1.0,
);
var byteData =
await capturedImage.toByteData(format: ImageByteFormat.png);
final bytes = byteData!.buffer.asUint8List();
var bitmap = img.decodeImage(bytes);
var red = 0;
var green = 0;
var blue = 0;
var pixels = 0;
final window = WidgetsBinding.instance.window;
final mediaQuery = MediaQueryData.fromWindow(window);
final statusHeight = mediaQuery.padding.top.clamp(20.0, 150.0);
/// Calculates the average color for the status bar
for (var y = 0; y < statusHeight.toInt(); y++) {
for (var x = 0; x < bitmap!.width; x++) {
var c = bitmap.getPixel(x, y);
pixels++;
red += img.getRed(c);
green += img.getGreen(c);
blue += img.getBlue(c);
}
}
var averageColor =
Color.fromRGBO(red ~/ pixels, green ~/ pixels, blue ~/ pixels, 1);
/// Computes the luminance. Note: This is computationally expensive.
var luminance = averageColor.computeLuminance();
/// Updates status bar color
if (luminance > 0.5) {
setDarkStatusBar();
} else {
setLightStatusBar();
}
从源码中我们可以看见它通过截取图片,提取 rgb 值进行比较处理,最后通过 if else 进行设置状态栏。有看过郭霖大神文章的同学会发现,这种实现方式和郭霖大神的大同小异。同时,也一样存在黑白参半时候难以设置的情况 ( 从效果图可看到 )。
四、完整代码
这里放上效果图的实现代码:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:statusbarz/statusbarz.dart';
void main() {
runApp(const MyApp());
SystemUiOverlayStyle systemUiOverlayStyle =
const SystemUiOverlayStyle(statusBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// return MaterialApp(
// debugShowCheckedModeBanner: false,
// routes: {
// '/white': (context) => WhitePage(),
// '/black': (context) => BlackPage(),
// '/p1': (context) => ImagePage1(),
// '/p2': (context) => ImagePage2(),
// '/two': (context) => TwoColorPage(),
// },
// home: WhitePage(),
// );
return StatusbarzCapturer(
child: MaterialApp(
debugShowCheckedModeBanner: false,
navigatorObservers: [Statusbarz.instance.observer],
routes: {
'/white': (context) => WhitePage(),
'/black': (context) => BlackPage(),
'/p1': (context) => ImagePage1(),
'/p2': (context) => ImagePage2(),
'/two': (context) => TwoColorPage(),
},
home: WhitePage(),
),
);
}
}
// white
class WhitePage extends StatefulWidget {
@override
State<WhitePage> createState() => _WhitePageState();
}
class _WhitePageState extends State<WhitePage> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: TextButton(
onPressed: () => Navigator.popAndPushNamed(context, "/black"),
child: const Text("黑色")),
);
}
}
// black
class BlackPage extends StatefulWidget {
@override
State<BlackPage> createState() => _BlackPageState();
}
class _BlackPageState extends State<BlackPage> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
child: TextButton(
onPressed: () => Navigator.popAndPushNamed(context, "/p1"),
child: const Text("图片1")),
);
}
}
// p1
class ImagePage1 extends StatefulWidget {
@override
State<ImagePage1> createState() => _ImagePage1State();
}
class _ImagePage1State extends State<ImagePage1> {
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/p1.jpg"), fit: BoxFit.cover)),
child: TextButton(
onPressed: () => Navigator.popAndPushNamed(context, "/p2"),
child: const Text("图片2")),
);
}
}
// p2
class ImagePage2 extends StatefulWidget {
@override
State<ImagePage2> createState() => _ImagePage2State();
}
class _ImagePage2State extends State<ImagePage2> {
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/images/pp.jpg"), fit: BoxFit.cover)),
child: TextButton(
onPressed: () => Navigator.popAndPushNamed(context, "/two"),
child: const Text("黑白")),
);
}
}
// two
class TwoColorPage extends StatefulWidget {
@override
State<TwoColorPage> createState() => _TwoColorPageState();
}
class _TwoColorPageState extends State<TwoColorPage> {
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(
color: Colors.black,
child: TextButton(
onPressed: () => Navigator.popAndPushNamed(context, "/white"),
child: const Text("白色")),
)),
Expanded(
child: Container(
color: Colors.white,
child: TextButton(
onPressed: () => Navigator.popAndPushNamed(context, "/white"),
child: const Text("白色")),
)),
],
);
}
}
最后
文章就到这,希望对你有帮助,欢迎评论,拜拜!