设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间14 现在时间14 缓存数据 “你总爱编织谎言,我总是配合表演。”

“你总爱编织谎言,我总是配合表演。” -- 配合

查看: 1390|回复: 3

基于Flutter实现动态高斯模糊的流程步骤

[复制链接]

  离线 

TA的专栏

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 打卡月天数:0
  • 打卡总奖励:70
  • 最近打卡:2025-06-04 01:46:07
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
26
主题
20
精华
0
金钱
149
积分
52
注册时间
2023-9-30
最后登录
2025-6-4

发表于 2024-2-25 11:46:15 | 显示全部楼层 |阅读模式
目录


  • 实现思路
  • 实现步骤

    • 创建一个工程
    • 创建自定义嵌套导航路由

      • App结构构建
      • 自定义嵌套导航

    • 高斯模糊
    • 封装BackdropFilter
    • 去除底部小白条

效果如下
1.webp


实现思路
  1. AppBar
复制代码
本质上是一个
  1. Widget
复制代码
,我们不直接通过
  1. Scaffold
复制代码
  1. appBar
复制代码
属性来设置
  1. AppBar
复制代码
,而是将我们需要的
  1. AppBar
复制代码
放在
  1. body
复制代码
属性里面,至于为什么不能放在
  1. appBar
复制代码
属性里面呢,最主要的原因是,
  1. Scaffold
复制代码
  1. appBar
复制代码
属性是顶级组件,在
  1. appBar
复制代码
以外的任何组件都无法覆盖或置于其下,而我们的主要实现思路是通过
  1. Stack
复制代码
组件的堆叠以及
  1. Positioned
复制代码
的位置设置来实现此效果

实现步骤


创建一个工程
  1. import 'package:flutter/material.dart';

  2. void main() {
  3.   runApp(const MyApp());
  4. }

  5. class MyApp extends StatelessWidget {
  6.   const MyApp({super.key});

  7.   @override
  8.   Widget build(BuildContext context) {
  9.     return MaterialApp(
  10.       title: 'Flutter Demo',
  11.       theme: ThemeData(
  12.         colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
  13.         useMaterial3: true,
  14.       ),
  15.     );
  16.   }
  17. }
复制代码
创建自定义嵌套导航路由


App结构构建

为了使代码更好的管理和维护,我们对页面使用三层架构模式,即分为
  1. state
复制代码
  1. logic
复制代码
  1. view
复制代码
三层,
  1. state
复制代码
层专注于数据,
  1. logic
复制代码
专注于页面的逻辑、
  1. view
复制代码
专注于页面的显示,这类似于
  1. Vue
复制代码
  1. <template>
复制代码
  1. <script>
复制代码
  1. <style>
复制代码
分层模式
  1. dependencies:
  2.   flutter:
  3.     sdk: flutter
  4.   # 路由管理
  5.   get: ^4.6.5
  6.   # 屏幕适配工具
  7.   flutter_screenutil: ^5.9.0
复制代码
  1. main.dart
复制代码
中更改为如下内容
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import 'package:get/get_navigation/src/root/get_material_app.dart';
  4. import 'package:text/router/routeconfig.dart';

  5. void main() {
  6.   runApp(const MyApp());
  7. }

  8. class MyApp extends StatelessWidget {
  9.   const MyApp({super.key});

  10.   @override
  11.   Widget build(BuildContext context) {
  12.     return ScreenUtilInit(
  13.       designSize: const Size(720, 1080),
  14.       minTextAdapt: true,
  15.       splitScreenMode: true,
  16.       builder: (context, child) {
  17.         return GetMaterialApp(
  18.           title: 'Flutter Demo',
  19.           // 隐藏debug
  20.           debugShowCheckedModeBanner: false,
  21.           theme: ThemeData(
  22.             colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueGrey),
  23.             useMaterial3: true,
  24.           ),
  25.           getPages: RouteConfig.getPages,
  26.           // 初始化主页
  27.           initialRoute: RouteConfig.main,
  28.         );
  29.       },
  30.     );
  31.   }
  32. }
复制代码
在lib中创建如下文件夹及文件
2.webp

router文件夹的内容
  1. routeconfig.dart
复制代码
  1. import 'package:get/get.dart';
  2. import 'package:text/pages/appmain/view.dart';

  3. class RouteConfig {
  4.   //主页面
  5.   static const String main = "/";

  6.   static final List<GetPage> getPages = [
  7.     GetPage(name: main, page: () => const AppMainPage()),
  8.   ];
  9. }
复制代码
appmain文件夹的内容
  1. state.dart
复制代码
  1. class AppMainState {
  2.   AppMainState();
  3. }
复制代码
  1. logic.dart
复制代码
  1. import 'package:get/get.dart';
  2. import 'state.dart';

  3. class AppMainLogic extends GetxController {
  4.   final AppMainState state = AppMainState();
  5. }
复制代码
  1. view.dart
复制代码
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'logic.dart';

  4. class AppMainPage extends StatefulWidget {
  5.   const AppMainPage({super.key});

  6.   @override
  7.   State<AppMainPage> createState() => _AppMainPageState();
  8. }

  9. class _AppMainPageState extends State<AppMainPage>
  10.     with TickerProviderStateMixin {
  11.   final logic = Get.put(AppMainLogic());
  12.   final state = Get.find<AppMainLogic>().state;

  13.   @override
  14.   void initState() {
  15.     super.initState();
  16.   }

  17.   @override
  18.   Widget build(BuildContext context) {
  19.     return Scaffold(
  20.       body: Container(),
  21.     );
  22.   }
  23. }
复制代码
  1. mainrouteconfig.dart
复制代码
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:text/pages/home/view.dart';

  4. class NestedController extends GetxController {
  5.   static const String home = "/homePage";
  6.   static const String explore = "/explorePage";
  7.   static const String musicLibrary = "/musicLibraryPage";

  8.   static final Map<String, Widget> pages = {
  9.     home: const HomePage(),
  10.   };

  11.   Route? onGenerateRoute(RouteSettings settings) {
  12.     GetPageRoute? getPageRoute;
  13.     pages.forEach((key, value) {
  14.       if (settings.name == key) {
  15.         getPageRoute = GetPageRoute(
  16.           settings: settings,
  17.           page: () => value,
  18.           transition: Transition.fade,
  19.         );
  20.       }
  21.     });

  22.     return getPageRoute;
  23.   }
  24. }
复制代码
home文件夹的内容
  1. state.dart
复制代码
  1. class HomeState {
  2.   HomeState() {
  3.     ///Initialize variables
  4.   }
  5. }
复制代码
  1. logic.dart
复制代码
  1. import 'package:get/get.dart';

  2. import 'state.dart';

  3. class HomeLogic extends GetxController {
  4.   final HomeState state = HomeState();
  5. }
复制代码
  1. view.dart
复制代码
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';

  3. import 'logic.dart';

  4. class HomePage extends StatefulWidget {
  5.   const HomePage({super.key});

  6.   @override
  7.   State<HomePage> createState() => _HomePageState();
  8. }

  9. class _HomePageState extends State<HomePage> {
  10.   final logic = Get.put(HomeLogic());
  11.   final state = Get.find<HomeLogic>().state;

  12.   @override
  13.   Widget build(BuildContext context) {
  14.     return Container();
  15.   }
  16. }
复制代码
创建完基础App架构模式后,我们开始自定义一个桥套导航,以便使我们在同一个页面内做页面的跳转,这类似于
  1. TabBar
复制代码
的效果
3.webp

接下来我们就可以使用我们的自定义嵌套导航了, 页面的嵌套导航总管理在
  1. pages/appmain
复制代码
文件夹下的
  1. mainrouteconfig.dart
复制代码
,然后我们接着完成嵌套导航结构

自定义嵌套导航

在需要用到嵌套导航的页面引入如下代码
  1. Navigator(
  2.                       key: Get.nestedKey(1),
  3.                       initialRoute: NestedController.home,
  4.                       onGenerateRoute: state.nestedController.onGenerateRoute,
  5.                     )
复制代码
如我们想在appmain下的view.dart中引入,首先state层建立我们需要的数据,这里我使用了一个第三方的TabBar组件,大家想用官方的组件也可以,只需稍做修改即可
导入第三方组件依赖
  1. dependencies:
  2.   flutter:
  3.     sdk: flutter
  4.   # 路由管理
  5.   get: ^4.6.5
  6.   # 屏幕适配工具
  7.   flutter_screenutil: ^5.9.0  # bruno ui组件  bruno: ^3.4.3
复制代码
  1. appmain/state.dart
复制代码
中修改如下
  1. import 'package:bruno/bruno.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:get/get.dart';
  4. import 'package:text/pages/appmain/mainrouteconfig.dart';

  5. class AppMainState {
  6.   late List<BadgeTab> tabs; // Tab列表
  7.   late TabController tabController; // Tab控制器

  8.   late NestedController nestedController;

  9.   AppMainState() {
  10.     // 添加顶部tab
  11.     tabs = [];
  12.     tabs.add(BadgeTab(text: "首页"));
  13.     tabs.add(BadgeTab(text: "发现"));
  14.     tabs.add(BadgeTab(text: "乐库"));

  15.     nestedController =
  16.         Get.put(NestedController(), permanent: true); // 创建嵌套导航控制器
  17.   }
  18. }
复制代码
  1. appmain/logic.dart
复制代码
修改如下,这里的
  1. void brnTabBarOnTap(brnState, index)
复制代码
函数中多了两个页面,大家可以自行在pages创建另外两个页面,也可以根据自己的需求进行更改
  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:text/pages/appmain/mainrouteconfig.dart';
  4. import 'state.dart';

  5. class AppMainLogic extends GetxController {
  6.   final AppMainState state = AppMainState();

  7.     void tabControllerInit(TickerProvider tickerProvider) {
  8.     state.tabController =
  9.         TabController(length: state.tabs.length, vsync: tickerProvider);
  10.   }

  11.   // 路由跳转控制
  12.   void brnTabBarOnTap(brnState, index) {
  13.     brnState.refreshBadgeState(index);
  14.     switch (index) {
  15.       case 0:
  16.         Get.toNamed(NestedController.home, id: 1, arguments: {});
  17.       case 1:
  18.         Get.toNamed(NestedController.explore, id: 1, arguments: {});
  19.       case 2:
  20.         Get.toNamed(NestedController.musicLibrary, id: 1, arguments: {});
  21.     }
  22.   }
  23. }
复制代码
我创建的页面如下,内容和
  1. home
复制代码
文件夹下的各文件类似,只是类名不同
4.webp

然后在
  1. appmain/mainrouteconfig.dart
复制代码
中稍作修改
  1.   static const String home = "/homePage";
  2.   static const String explore = "/explorePage";
  3.   static const String musicLibrary = "/musicLibraryPage";

  4.   static final Map<String, Widget> pages = {
  5.     home: const HomePage(),
  6.     explore: const ExplorePage(),
  7.     musicLibrary: const MusicLibraryPage(),
  8.   };
复制代码
然后我们就可以在
  1. appmain/view
复制代码
中使用了,为了更好的适配多分辨率屏幕的影响,我们可以先定义一个页面缩放小工具,我们在lib目录下创建一个
  1. common
复制代码
文件夹,文件夹下创建一个
  1. utils
复制代码
文件夹,里面有个工具类
  1. screenadaptor.dart
复制代码
5.webp

工具类内容如下,此工具类是为了更好的管理横竖屏的像素而建立的类
  1. import 'package:flutter_screenutil/flutter_screenutil.dart';

  2. class ScreenAdaptor {

  3.   ScreenAdaptor();
  4.   double getLengthByOrientation(double horizon, double vertical)
  5.   {
  6.     return ScreenUtil().orientation.index == 0 ? horizon : vertical;
  7.   }
  8. }

  9. final ScreenAdaptor screenAdaptor = ScreenAdaptor();
复制代码
这时,我们就可以在
  1. appmain/view.dart
复制代码
中添加嵌套导航的组件以及一些样式的优化,代码如下
  1. import 'package:bruno/bruno.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:flutter_screenutil/flutter_screenutil.dart';
  5. import 'package:get/get.dart';
  6. import 'package:text/common/utils/screenadaptor.dart';
  7. import 'logic.dart';

  8. class AppMainPage extends StatefulWidget {
  9.   const AppMainPage({super.key});

  10.   @override
  11.   State<AppMainPage> createState() => _AppMainPageState();
  12. }

  13. class _AppMainPageState extends State<AppMainPage>
  14.     with TickerProviderStateMixin {
  15.   final logic = Get.put(AppMainLogic());
  16.   final state = Get.find<AppMainLogic>().state;

  17.   @override
  18.   void initState() {
  19.     super.initState();
  20.     logic.tabControllerInit(this);
  21.   }

  22.     @override
  23.   void dispose() {
  24.     super.dispose();
  25.     state.tabController.dispose();
  26.     state.nestedController.dispose();
  27.   }

  28.   @override
  29.   Widget build(BuildContext context) {
  30.     return Scaffold(
  31.       body: ConstrainedBox(
  32.         constraints: const BoxConstraints.expand(),
  33.         child: Stack(
  34.           children: [
  35.             Positioned(
  36.               // 不可以修改
  37.               top: screenAdaptor.getLengthByOrientation(35.h, 35.h),
  38.               child: SizedBox(
  39.                 width: ScreenUtil().screenWidth,
  40.                 height: ScreenUtil().screenHeight,
  41.                 child: Navigator(
  42.                   key: Get.nestedKey(1),
  43.                   initialRoute: "/homePage",
  44.                   onGenerateRoute: state.nestedController.onGenerateRoute,
  45.                 ),
  46.               ),
  47.             ),
  48.             Positioned(
  49.               top: screenAdaptor.getLengthByOrientation(-10.h, -10.h),
  50.               child: SizedBox(
  51.                 width: ScreenUtil().screenWidth,
  52.                 child: BrnAppBar(
  53.                   // 状态栏和底部栏样式
  54.                   systemOverlayStyle: const SystemUiOverlayStyle(
  55.                     statusBarColor: Colors.transparent,
  56.                     statusBarIconBrightness: Brightness.dark,
  57.                     statusBarBrightness: Brightness.dark,
  58.                     systemNavigationBarColor: Colors.transparent,
  59.                   ),
  60.                   primary: true,
  61.                   // 不显示底部分割线
  62.                   showDefaultBottom: false,
  63.                   backgroundColor: Colors.transparent,
  64.                   title: BrnTabBar(
  65.                     indicatorColor: Colors.transparent,
  66.                     backgroundcolor: Colors.transparent,
  67.                     padding: EdgeInsets.fromLTRB(
  68.                         screenAdaptor.getLengthByOrientation(70.w, 160.w),
  69.                         0,
  70.                         screenAdaptor.getLengthByOrientation(70.w, 160.w),
  71.                         0),
  72.                     mode: BrnTabBarBadgeMode.origin,
  73.                     controller: state.tabController,
  74.                     tabs: state.tabs,
  75.                     onTap: logic.brnTabBarOnTap,
  76.                     labelStyle: const TextStyle(
  77.                       backgroundColor: Colors.transparent,
  78.                       fontWeight: FontWeight.bold,
  79.                     ),
  80.                     unselectedLabelStyle: const TextStyle(
  81.                       backgroundColor: Colors.transparent,
  82.                       fontWeight: FontWeight.bold,
  83.                     ),
  84.                   ),
  85.                   leadingWidth:
  86.                       screenAdaptor.getLengthByOrientation(100.w, 50.w),
  87.                   leading: IconButton(
  88.                     onPressed: (){}, // 打开侧边栏
  89.                     icon: const Icon(Icons.menu),
  90.                   ),
  91.                   themeData: BrnAppBarConfig(
  92.                     itemSpacing: 0,
  93.                     leftAndRightPadding: 0,
  94.                   ),
  95.                   actions: <Widget>[
  96.                     IconButton(
  97.                       onPressed: () {},
  98.                       icon: const Icon(Icons.search),
  99.                     ),
  100.                   ],
  101.                 ),
  102.               ),
  103.             ),
  104.           ],
  105.         ),
  106.       ),
  107.     );
  108.   }
  109. }
复制代码
然后一运行,就可以发现大体效果出来了
6.webp

有了AppBar框架后,我们就可以实现我们的动态高斯模糊了

高斯模糊

组件的背景高斯模糊我们可以通过
  1. BackdropFilter
复制代码
这一个类来做实现,基于此,我封装了一个类来便于我们使用
我们在
  1. lib
复制代码
目录下新建一个
  1. component
复制代码
文件夹,里面创建一个
  1. BlurRectWidget
复制代码
的类
7.webp

内容为
  1. import 'dart:ui';

  2. import 'package:flutter/material.dart';

  3. class BlurRectWidget extends StatelessWidget {
  4.   final Widget _widget;
  5.   final double singmaX;
  6.   final double singmaY;

  7.   const BlurRectWidget(this._widget, {super.key, required this.singmaX, required this.singmaY});

  8.   @override
  9.   Widget build(BuildContext context) {
  10.     return ClipRRect(
  11.       child: BackdropFilter(
  12.         filter: ImageFilter.blur(
  13.           sigmaX: singmaX,
  14.           sigmaY: singmaY,
  15.         ),
  16.         child: _widget,
  17.       ),
  18.     );
  19.   }
  20. }
复制代码
使用方法
  1. BlurRectWidget(
  2.                   singmaX: 20,
  3.                   singmaY: 20,
  4.                   widget // 这里换成你想包裹的widget
  5.                )
复制代码
比如我们在
  1. appmain/view.dart
复制代码
中将
  1. BrnAppBar
复制代码
包裹起来
  1. BlurRectWidget(
  2.                   singmaX: 20,
  3.                   singmaY: 20,
  4.                   BrnAppBar( ... )   
  5.                 ),
复制代码
运行查看效果
8.webp

可以看到已有高斯模糊效果,那是不是动态的呢,我们在
  1. home/view
复制代码
里面添加一些内容来验证
  1. home/view.dart
复制代码
内容修改如下
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_screenutil/flutter_screenutil.dart';
  3. import 'package:get/get.dart';
  4. import 'package:text/common/utils/screenadaptor.dart';

  5. import 'logic.dart';

  6. class HomePage extends StatefulWidget {
  7.   const HomePage({super.key});

  8.   @override
  9.   State<HomePage> createState() => _HomePageState();
  10. }

  11. class _HomePageState extends State<HomePage> {
  12.   final logic = Get.put(HomeLogic());
  13.   final state = Get.find<HomeLogic>().state;

  14.   @override
  15.   Widget build(BuildContext context) {
  16.     return Container(
  17.       padding: EdgeInsets.fromLTRB(
  18.         screenAdaptor.getLengthByOrientation(24.h, 20.h),
  19.         0,
  20.         screenAdaptor.getLengthByOrientation(24.h, 20.h),
  21.         0,
  22.       ),
  23.       child: Stack(
  24.         children: [
  25.           SizedBox(
  26.             // 占满屏幕
  27.             width: ScreenUtil().screenWidth,
  28.             height: ScreenUtil().screenHeight,
  29.             child: ListView(
  30.               children: [
  31.                 // 占位
  32.                 SizedBox(
  33.                   height: screenAdaptor.getLengthByOrientation(45.h, 45.h),
  34.                 ),
  35.                 Container(
  36.                   color: Colors.blue,
  37.                   height: 1000,
  38.                   width: 100,
  39.                   child: Image.network(
  40.                     "https://t.mwm.moe/mp/",
  41.                     fit: BoxFit.cover,
  42.                   ),
  43.                 ),
  44.               ],
  45.             ),
  46.           ),
  47.         ],
  48.       ),
  49.     );
  50.   }
  51. }
复制代码
重新运行效果
9.webp

可以看到已经有了动态高斯模糊 自此高级观感的AppBar已经实现完毕

封装BackdropFilter

为了能像使用CSS里的filter一样方便,我封装了一个BackdropCSSFilter类, 此类基于https://github.com/iofod/flutter_css_filter做修改,因为原作者的CSSFilter对组件进行模糊而不是对背景进行模糊,所以我做修改了
  1. common/utils
复制代码
目录下创建如下文件
10.webp
  1. base.dart
复制代码
内容如下
  1. import 'dart:math' as math;
  2. import 'css_filter.dart';
  3. import 'utils.dart';

  4. class BackdropFilterMatrix {
  5.   /// Check: https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/contrast()
  6.   static contrast({required List<double> matrix, required double value}) {
  7.     double v = value;
  8.     double b = (1.0 - value) * 0.5 * 255.0; // 0.5*255 => 127

  9.     return multiplyMatrix5(matrix, <double>[
  10.       v,
  11.       0,
  12.       0,
  13.       0,
  14.       b,
  15.       0,
  16.       v,
  17.       0,
  18.       0,
  19.       b,
  20.       0,
  21.       0,
  22.       v,
  23.       0,
  24.       b,
  25.       0,
  26.       0,
  27.       0,
  28.       1,
  29.       0,
  30.       0,
  31.       0,
  32.       0,
  33.       0,
  34.       1
  35.     ]);
  36.   }

  37.   /// Formula from: https://www.w3.org/TR/filter-effects-1/#grayscaleEquivalent
  38.   static grayscale({required List<double> matrix, required double value}) {
  39.     double v = 1.0 - value;
  40.     double lumR = 0.2126;
  41.     double lumG = 0.7152;
  42.     double lumB = 0.0722;

  43.     return multiplyMatrix5(matrix, <double>[
  44.       (lumR + (1 - lumR) * v),
  45.       (lumG - lumG * v),
  46.       (lumB - lumB * v),
  47.       0,
  48.       0,
  49.       (lumR - lumR * v),
  50.       (lumG + (1 - lumG) * v),
  51.       (lumB - lumB * v),
  52.       0,
  53.       0,
  54.       (lumR - lumR * v),
  55.       (lumG - lumG * v),
  56.       (lumB + (1 - lumB) * v),
  57.       0,
  58.       0,
  59.       0,
  60.       0,
  61.       0,
  62.       1,
  63.       0,
  64.       0,
  65.       0,
  66.       0,
  67.       0,
  68.       1
  69.     ]);
  70.   }

  71.   /// Formula from: https://www.w3.org/TR/filter-effects-1/#sepiaEquivalent
  72.   static sepia({required List<double> matrix, required double value}) {
  73.     double v = 1.0 - value;

  74.     return multiplyMatrix5(matrix, <double>[
  75.       (0.393 + 0.607 * v),
  76.       (0.769 - 0.769 * v),
  77.       (0.189 - 0.189 * v),
  78.       0,
  79.       0,
  80.       (0.349 - 0.349 * v),
  81.       (0.686 + 0.314 * v),
  82.       (0.168 - 0.168 * v),
  83.       0,
  84.       0,
  85.       (0.272 - 0.272 * v),
  86.       (0.534 - 0.534 * v),
  87.       (0.131 + 0.869 * v),
  88.       0,
  89.       0,
  90.       0,
  91.       0,
  92.       0,
  93.       1,
  94.       0,
  95.       0,
  96.       0,
  97.       0,
  98.       0,
  99.       1
  100.     ]);
  101.   }

  102.   /// Check: https://www.geeksforgeeks.org/css-invert-function/
  103.   static invert({required List<double> matrix, required double value}) {
  104.     // v * (255 - n) + (1 - v) * n => (1 - 2v) * n + 255 * v
  105.     double v = value * 255.0;
  106.     double k = 1.0 - 2.0 * value;

  107.     // The fifth column n is 255.
  108.     return multiplyMatrix5(matrix, <double>[
  109.       k,
  110.       0,
  111.       0,
  112.       0,
  113.       v,
  114.       0,
  115.       k,
  116.       0,
  117.       0,
  118.       v,
  119.       0,
  120.       0,
  121.       k,
  122.       0,
  123.       v,
  124.       0,
  125.       0,
  126.       0,
  127.       1,
  128.       0,
  129.       0,
  130.       0,
  131.       0,
  132.       0,
  133.       1
  134.     ]);
  135.   }

  136.   /// Check: https://stackoverflow.com/questions/64639589/how-to-adjust-hue-saturation-and-brightness-of-an-image-in-flutter
  137.   static hue({required List<double> matrix, required double value}) {
  138.     double v = math.pi * (value / 180.0);
  139.     double cosVal = math.cos(v);
  140.     double sinVal = math.sin(v);
  141.     double lumR = 0.213;
  142.     double lumG = 0.715;
  143.     double lumB = 0.072;

  144.     return multiplyMatrix5(matrix, <double>[
  145.       (lumR + (cosVal * (1 - lumR))) + (sinVal * (-lumR)),
  146.       (lumG + (cosVal * (-lumG))) + (sinVal * (-lumG)),
  147.       (lumB + (cosVal * (-lumB))) + (sinVal * (1 - lumB)),
  148.       0,
  149.       0,
  150.       (lumR + (cosVal * (-lumR))) + (sinVal * 0.143),
  151.       (lumG + (cosVal * (1 - lumG))) + (sinVal * 0.14),
  152.       (lumB + (cosVal * (-lumB))) + (sinVal * (-0.283)),
  153.       0,
  154.       0,
  155.       (lumR + (cosVal * (-lumR))) + (sinVal * (-(1 - lumR))),
  156.       (lumG + (cosVal * (-lumG))) + (sinVal * lumG),
  157.       (lumB + (cosVal * (1 - lumB))) + (sinVal * lumB),
  158.       0,
  159.       0,
  160.       0,
  161.       0,
  162.       0,
  163.       1,
  164.       0,
  165.       0,
  166.       0,
  167.       0,
  168.       0,
  169.       1
  170.     ]);
  171.   }

  172.   static brightness({required List<double> matrix, required double value}) {
  173.     double v = value;

  174.     return multiplyMatrix5(matrix, <double>[
  175.       v,
  176.       0,
  177.       0,
  178.       0,
  179.       0,
  180.       0,
  181.       v,
  182.       0,
  183.       0,
  184.       0,
  185.       0,
  186.       0,
  187.       v,
  188.       0,
  189.       0,
  190.       0,
  191.       0,
  192.       0,
  193.       1,
  194.       0,
  195.       0,
  196.       0,
  197.       0,
  198.       0,
  199.       1
  200.     ]);
  201.   }

  202.   /// Check: https://docs.rainmeter.net/tips/colormatrix-guide/
  203.   static saturate({required List<double> matrix, required double value}) {
  204.     return BackdropFilterMatrix.grayscale(matrix: matrix, value: 1.0 - value);
  205.   }

  206.   static opacity({required List<double> matrix, required double value}) {
  207.     return multiplyMatrix5(matrix, <double>[
  208.       1,
  209.       0,
  210.       0,
  211.       0,
  212.       0,
  213.       0,
  214.       1,
  215.       0,
  216.       0,
  217.       0,
  218.       0,
  219.       0,
  220.       1,
  221.       0,
  222.       0,
  223.       0,
  224.       0,
  225.       0,
  226.       value,
  227.       0,
  228.       0,
  229.       0,
  230.       0,
  231.       0,
  232.       1
  233.     ]);
  234.   }
  235. }

  236. // Allows matrix multiplication.
  237. final filterTypeMap = {
  238.   'contrast': BackdropFilterMatrix.contrast,
  239.   'grayscale': BackdropFilterMatrix.grayscale,
  240.   'hueRotate': BackdropFilterMatrix.hue,
  241.   'brightness': BackdropFilterMatrix.brightness,
  242.   'saturate': BackdropFilterMatrix.saturate,
  243.   'opacity': BackdropFilterMatrix.opacity,
  244. };

  245. // Not superimposed on the original matrix.
  246. final filterAloneMap = {
  247.   'sepia': BackdropCSSFilter.sepia,
  248.   'invert': BackdropCSSFilter.invert,
  249.   'blur': BackdropCSSFilter.blur
  250. };
复制代码
css_filter.dart
  1. /// CSS filter for flutter
  2. /// Author: qkorbit
  3. /// Released under BSD-3-Clause License.
  4. library css_filter;

  5. export 'filter.dart';
  6. export 'presets.dart';
  7. export 'base.dart' show BackdropFilterMatrix;
  8. export 'utils.dart' show BackdropCSSFilterMatrix;
复制代码
filter.dart
  1. import 'dart:ui';
  2. import 'package:flutter/material.dart';
  3. import 'utils.dart';
  4. import 'base.dart';

  5. /// Use CSS filter effects on flutter's Widget. All CSS filters are implemented except `drop-shadow()`.
  6. /// `drop-shadow()` should be replaced by the [BoxShadow](https://api.flutter.dev/flutter/painting/BoxShadow-class.html) or [Shadow](https://api.flutter.dev/flutter/dart-ui/Shadow-class.html) widget.
  7. ///
  8. /// Example:
  9. ///
  10. /// ```dart
  11. /// CSSFilter.contrast(child: const Text('foo'), value: 1.2);
  12. /// ```
  13. ///
  14. /// Support effects:
  15. /// * contrast()
  16. /// * grayscale()
  17. /// * sepia()
  18. /// * hueRotate()
  19. /// * brightness()
  20. /// * saturate()
  21. /// * invert()
  22. /// * blur()
  23. /// * opacity()
  24. class BackdropCSSFilter {
  25.   /// Adjusts the contrast of the input widget.
  26.   /// A value under 1.0 decreases the contrast, while a value over 1.0 increases it.
  27.   /// A value of 0.0 will make it completely gray.
  28.   /// Default value is 1.0.
  29.   static Widget contrast({required Widget child, required double value}) {
  30.     if (!isNotDefault(value)) return child;

  31.     return execFilterSample(
  32.         BackdropFilterMatrix.contrast(matrix: baseMatrix(), value: value),
  33.         child);
  34.   }

  35.   /// Converts the input widget to grayscale.
  36.   /// Values between 0.0 and 1.0 are linear multipliers on the effect.
  37.   /// A value of 1.0 is completely grayscale.
  38.   /// Default value is 0.0.
  39.   static Widget grayscale({required Widget child, required double value}) {
  40.     if (!isNotNegative(value)) return child;

  41.     return execFilterSample(
  42.         BackdropFilterMatrix.grayscale(matrix: baseMatrix(), value: value),
  43.         child);
  44.   }

  45.   /// Converts the input widget to sepia, giving it a warmer, more yellow/brown appearance.
  46.   /// Values between 0.0 and 1.0 are linear multipliers on the effect.
  47.   /// A value of 1.0 is completely sepia.
  48.   /// Default value is 0.0.
  49.   static Widget sepia({required Widget child, required double value}) {
  50.     if (!isNotNegative(value)) return child;

  51.     return execFilterSample(
  52.         BackdropFilterMatrix.sepia(matrix: baseMatrix(), value: value), child);
  53.   }

  54.   /// Rotates the [hue](https://en.wikipedia.org/wiki/Hue) of the input widget.
  55.   /// A positive hue rotation increases the hue value, while a negative rotation decreases the hue value.
  56.   /// @parmas value: A value of rotate angle.
  57.   /// Default value is 0.0.
  58.   static Widget hueRotate({required Widget child, required double value}) {
  59.     if (value == 0.0) return child;

  60.     return execFilterSample(
  61.         BackdropFilterMatrix.hue(matrix: baseMatrix(), value: value), child);
  62.   }

  63.   /// Apply a linear multiplier to the input widget, making it appear brighter or darker.
  64.   /// A value under 1.0 darkens the Widget, while a value over 1.0 brightens it.
  65.   /// A value of 0.0 will make it completely black.
  66.   /// Default value is 1.0.
  67.   static Widget brightness({required Widget child, required double value}) {
  68.     if (!isNotDefault(value)) return child;

  69.     return execFilterSample(
  70.         BackdropFilterMatrix.brightness(matrix: baseMatrix(), value: value),
  71.         child);
  72.   }

  73.   /// Super-saturates or desaturates the input widget.
  74.   /// A value under 1.0 desaturates the Widget, while a value over 1.0 super-saturates it.
  75.   /// A value of 0.0 is completely unsaturated.
  76.   /// Default value is 1.0.
  77.   static Widget saturate({required Widget child, required double value}) {
  78.     if (!isNotDefault(value)) return child;

  79.     return execFilterSample(
  80.         BackdropFilterMatrix.saturate(matrix: baseMatrix(), value: value),
  81.         child);
  82.   }

  83.   /// Inverts the color of input widget.
  84.   /// Values between 0.0 and 1.0 are linear multipliers on the effect.
  85.   /// A value of 1.0 is completely inverted.
  86.   /// Default value is 0.0.
  87.   static Widget invert({required Widget child, required double value}) {
  88.     if (!isNotNegative(value)) return child;

  89.     return execFilterSample(
  90.         BackdropFilterMatrix.invert(matrix: baseMatrix(), value: value), child);
  91.   }

  92.   /// Apply a Gaussian blur to the input widget.
  93.   /// A larger value will create more blur on input widget.
  94.   /// @parmas value: A value of blur radius.
  95.   /// Default value is 0.0.
  96.   static Widget blur({required Widget child, required double value}) {
  97.     if (!isNotNegative(value)) return child;

  98.     return ClipRRect(
  99.       child: BackdropFilter(
  100.           filter: ImageFilter.blur(
  101.               sigmaX: value, sigmaY: value, tileMode: TileMode.decal),
  102.           child: child),
  103.     );
  104.   }

  105.   /// Apply transparency to input widget.
  106.   /// Values between 0.0 and 1.0 are linear multipliers on the effect.
  107.   /// A value of 0.0 is completely transparent.
  108.   /// Default value is 1.0.
  109.   static Widget opacity({required Widget child, required double value}) {
  110.     if (!isNotDefault(value)) return child;

  111.     return execFilterSample(
  112.         BackdropFilterMatrix.opacity(matrix: baseMatrix(), value: value),
  113.         child);
  114.   }

  115.   /// A quick and efficient way to apply multiple filters to the input widget.
  116.   /// You can use any combination of these filter effects.
  117.   ///
  118.   /// Example:
  119.   ///
  120.   /// ```dart
  121.   /// CSSFilter.apply(child: const Text('Hello World!'), value: CSSFilterMatrix().contrast(0.5).blur(3.0));
  122.   /// CSSFilter.apply(child: const Text('Hello World!'), value: CSSFilterMatrix().brightness(1.2).saturate(1.5));
  123.   /// ```
  124.   ///
  125.   static Widget apply(
  126.       {required Widget child, required BackdropCSSFilterMatrix value}) {
  127.     List<double> matrix = baseMatrix();
  128.     Widget tree = child;
  129.     bool canMerge = false;

  130.     value.conf.forEach((K, V) {
  131.       var fn = filterTypeMap[K];

  132.       if (fn != null) {
  133.         matrix = fn(matrix: matrix, value: V);
  134.         canMerge = true;
  135.       } else {
  136.         // merge layers once
  137.         if (canMerge) {
  138.           tree = ClipRRect(
  139.             child: BackdropFilter(
  140.               filter: toColorFilterMatrix(matrix),
  141.               child: tree,
  142.             ),
  143.           );

  144.           canMerge = false;
  145.         }

  146.         var alone = filterAloneMap[K];

  147.         tree = alone!(child: tree, value: V);
  148.         // reset matrix
  149.         matrix = baseMatrix();
  150.       }
  151.     });

  152.     if (!canMerge) return tree;

  153.     return ClipRRect(
  154.       child: BackdropFilter(
  155.         filter: toColorFilterMatrix(matrix),
  156.         child: tree,
  157.       ),
  158.     );
  159.   }
  160. }
复制代码
presets.dart
  1. import 'package:flutter/material.dart';

  2. import 'filter.dart';
  3. import 'utils.dart';

  4. /// Added more preset filter effects to CSSFilter.
  5. /// The current version adds instagram filter package, the values mainly refer to [CSSgram](https://github.com/una/CSSgram), partly refer to [instagram.css](https://github.com/picturepan2/instagram.css).
  6. ///
  7. /// Example:
  8. ///
  9. /// ```dart
  10. /// CSSFilterPresets.insAshby(child: const Text('foo'));
  11. /// CSSFilterPresets.insHelena(child: const Text('bar'));
  12. /// ```
  13. ///
  14. /// Support effects:
  15. /// * ins1977()
  16. /// * ins1977V2()
  17. /// * insAden()
  18. /// * insAmaro()
  19. /// * insAshby()
  20. /// * insBrannan()
  21. /// * insBrooklyn()
  22. /// * insClarendon()
  23. /// * insDogpatch()
  24. /// * insEarlybird()
  25. /// * insGingham()
  26. /// * insHelena()
  27. /// * insHudson()
  28. /// * insInkwell()
  29. /// * insInkwellV2()
  30. /// * insJuno()
  31. /// * insKelvin()
  32. /// * insLark()
  33. /// * insLofi()
  34. /// * insLudwig()
  35. /// * insMaven()
  36. /// * insMayfair()
  37. /// * insMoon()
  38. /// * insMoonV2()
  39. /// * insNashville()
  40. /// * insNashvilleV2()
  41. /// * insPerpetua()
  42. /// * insPoprocket()
  43. /// * insReyes()
  44. /// * insRise()
  45. /// * insSierra()
  46. /// * insSkyline()
  47. /// * insSlumber()
  48. /// * insStinson()
  49. /// * insSutro()
  50. /// * insToaster()
  51. /// * insToasterV2()
  52. /// * insValencia()
  53. /// * insVesper()
  54. /// * insWalden()
  55. /// * insWaldenV2()
  56. /// * insWillow()
  57. /// * insXpro2()
  58. ///
  59. class CSSFilterPresets {
  60.   /// A quick and efficient way to apply preset effects to the input widget.
  61.   /// You can even adjust the intensity of the preset effects.
  62.   ///
  63.   /// Example:
  64.   ///
  65.   /// ```dart
  66.   /// CSSFilterPresets.apply(
  67.   ///   child: const Text('foo'),
  68.   ///   value: CSSFilterPresets.insMaven,
  69.   ///   strength: 0.6
  70.   /// );
  71.   /// ```
  72.   ///
  73.   /// If the input widget is transparent, then the `alphaBlending` parameter should be set to adjust the [Alpha Compositing](https://ciechanow.ski/alpha-compositing/) to avoid gross overlay of transparency.
  74.   /// In general, `alphaBlending` is set to the same opacity value as the input widget. If the opacity of the input widget is unknown, the `alphaBlending` value is set according to the actual situation.
  75.   ///
  76.   static Widget apply(
  77.       {required Widget child,
  78.       required Function value,
  79.       double strength = 1.0,
  80.       double alphaBlending = 1.0}) {
  81.     if (strength <= 0.0) return child;
  82.     if (strength >= 1.0) strength = 1.0;

  83.     Widget filtered = value(child: child);

  84.     if (strength == 1.0) return filtered;
  85.     if (alphaBlending > 1.0) alphaBlending = 1.0;
  86.     if (alphaBlending < 0.0) alphaBlending = 0.0;

  87.     Widget tree = Stack(children: [
  88.       Positioned(child: child),
  89.       Positioned(
  90.           child:
  91.               IgnorePointer(child: Opacity(opacity: strength, child: filtered)))
  92.     ]);

  93.     if (alphaBlending == 1.0) return tree;

  94.     return Opacity(
  95.         opacity: 1.0 - (1.0 - alphaBlending) * strength, child: tree);
  96.   }

  97.   static Widget origin({required Widget child}) {
  98.     return child;
  99.   }

  100.   static Widget ins1977({required Widget child}) {
  101.     return ShaderMask(
  102.       shaderCallback:
  103.           execShaderDirectSample(const Color.fromRGBO(243, 106, 188, 0.3)),
  104.       blendMode: BlendMode.screen,
  105.       child: BackdropCSSFilter.apply(
  106.           child: child,
  107.           value: BackdropCSSFilterMatrix().contrast(1.1).brightness(1.1).saturate(1.3)),
  108.     );
  109.   }

  110.   static Widget ins1977V2({required Widget child}) {
  111.     return BackdropCSSFilter.apply(
  112.         child: child,
  113.         value: BackdropCSSFilterMatrix().sepia(0.5).hueRotate(-30.0).saturate(1.4));
  114.   }

  115.   static Widget insAden({required Widget child}) {
  116.     return ShaderMask(
  117.       shaderCallback: execShaderLinearSample([
  118.         const Color.fromRGBO(66, 10, 14, 0.2),
  119.         const Color.fromRGBO(0, 0, 0, 0.0)
  120.       ], Alignment.centerRight),
  121.       blendMode: BlendMode.darken,
  122.       child: BackdropCSSFilter.apply(
  123.           child: child,
  124.           value: BackdropCSSFilterMatrix()
  125.               .hueRotate(-20.0)
  126.               .contrast(0.9)
  127.               .saturate(0.85)
  128.               .brightness(1.2)),
  129.     );
  130.   }

  131.   static Widget insAmaro({required Widget child}) {
  132.     return ShaderMask(
  133.       shaderCallback:
  134.           execShaderDirectSample(const Color.fromRGBO(125, 105, 24, 0.2)),
  135.       blendMode: BlendMode.overlay,
  136.       child: BackdropCSSFilter.apply(
  137.           child: child,
  138.           value: BackdropCSSFilterMatrix()
  139.               .sepia(0.35)
  140.               .contrast(1.1)
  141.               .brightness(1.2)
  142.               .saturate(1.3)),
  143.     );
  144.   }

  145.   static Widget insAshby({required Widget child}) {
  146.     return ShaderMask(
  147.       shaderCallback:
  148.           execShaderDirectSample(const Color.fromRGBO(125, 105, 24, 0.35)),
  149.       blendMode: BlendMode.lighten,
  150.       child: BackdropCSSFilter.apply(
  151.           child: child,
  152.           value: BackdropCSSFilterMatrix().sepia(0.5).contrast(1.2).saturate(1.8)),
  153.     );
  154.   }

  155.   static Widget insBrannan({required Widget child}) {
  156.     return ShaderMask(
  157.       shaderCallback:
  158.           execShaderDirectSample(const Color.fromRGBO(161, 44, 199, 0.31)),
  159.       blendMode: BlendMode.lighten,
  160.       child: BackdropCSSFilter.apply(
  161.           child: child, value: BackdropCSSFilterMatrix().sepia(0.5).contrast(1.4)),
  162.     );
  163.   }

  164.   static Widget insBrooklyn({required Widget child}) {
  165.     return ShaderMask(
  166.       shaderCallback: execShaderRadialSample([
  167.         const Color.fromRGBO(168, 223, 193, 0.4),
  168.         const Color.fromRGBO(196, 183, 200, 1.0)
  169.       ], [
  170.         0.0,
  171.         0.7
  172.       ]),
  173.       blendMode: BlendMode.overlay,
  174.       child: BackdropCSSFilter.apply(
  175.           child: child, value: BackdropCSSFilterMatrix().contrast(0.9).brightness(1.1)),
  176.     );
  177.   }

  178.   static Widget insClarendon({required Widget child}) {
  179.     return ShaderMask(
  180.       shaderCallback:
  181.           execShaderDirectSample(const Color.fromRGBO(127, 187, 227, 0.2)),
  182.       blendMode: BlendMode.overlay,
  183.       child: BackdropCSSFilter.apply(
  184.           child: child, value: BackdropCSSFilterMatrix().contrast(1.2).saturate(1.35)),
  185.     );
  186.   }

  187.   static Widget insDogpatch({required Widget child}) {
  188.     return BackdropCSSFilter.apply(
  189.         child: child,
  190.         value: BackdropCSSFilterMatrix().sepia(0.35).saturate(1.1).contrast(1.5));
  191.   }

  192.   static Widget insEarlybird({required Widget child}) {
  193.     return ShaderMask(
  194.       shaderCallback: execShaderRadialSample([
  195.         const Color.fromRGBO(208, 186, 142, 1.0),
  196.         const Color.fromRGBO(54, 3, 9, 1.0),
  197.         const Color.fromRGBO(29, 2, 16, 1.0)
  198.       ], [
  199.         0.2,
  200.         0.85,
  201.         1.0
  202.       ]),
  203.       blendMode: BlendMode.overlay,
  204.       child: BackdropCSSFilter.apply(
  205.           child: child, value: BackdropCSSFilterMatrix().contrast(0.9).sepia(0.2)),
  206.     );
  207.   }

  208.   static Widget insGingham({required Widget child}) {
  209.     return ShaderMask(
  210.       shaderCallback:
  211.           execShaderDirectSample(const Color.fromRGBO(230, 230, 250, 1.0)),
  212.       blendMode: BlendMode.softLight,
  213.       child: BackdropCSSFilter.apply(
  214.           child: child,
  215.           value: BackdropCSSFilterMatrix().brightness(1.05).hueRotate(-10.0)),
  216.     );
  217.   }

  218.   static Widget insHelena({required Widget child}) {
  219.     return ShaderMask(
  220.       shaderCallback:
  221.           execShaderDirectSample(const Color.fromRGBO(158, 175, 30, 0.25)),
  222.       blendMode: BlendMode.overlay,
  223.       child: BackdropCSSFilter.apply(
  224.           child: child,
  225.           value: BackdropCSSFilterMatrix()
  226.               .sepia(0.5)
  227.               .contrast(1.05)
  228.               .brightness(1.05)
  229.               .saturate(1.35)),
  230.     );
  231.   }

  232.   static Widget insHudson({required Widget child}) {
  233.     return ShaderMask(
  234.       shaderCallback: execShaderRadialSample([
  235.         const Color.fromRGBO(166, 177, 255, 0.5),
  236.         const Color.fromRGBO(52, 33, 52, 0.5)
  237.       ], [
  238.         0.5,
  239.         1.0
  240.       ]),
  241.       blendMode: BlendMode.multiply,
  242.       child: BackdropCSSFilter.apply(
  243.           child: child,
  244.           value: BackdropCSSFilterMatrix().brightness(1.2).contrast(0.9).saturate(1.1)),
  245.     );
  246.   }

  247.   static Widget insInkwell({required Widget child}) {
  248.     return BackdropCSSFilter.apply(
  249.         child: child,
  250.         value: BackdropCSSFilterMatrix()
  251.             .sepia(0.3)
  252.             .contrast(1.1)
  253.             .brightness(1.1)
  254.             .grayscale(1.0));
  255.   }

  256.   static Widget insInkwellV2({required Widget child}) {
  257.     return BackdropCSSFilter.apply(
  258.         child: child,
  259.         value:
  260.         BackdropCSSFilterMatrix().brightness(1.25).contrast(0.85).grayscale(1.0));
  261.   }

  262.   static Widget insJuno({required Widget child}) {
  263.     return ShaderMask(
  264.       shaderCallback:
  265.           execShaderDirectSample(const Color.fromRGBO(127, 187, 227, 0.2)),
  266.       blendMode: BlendMode.overlay,
  267.       child: BackdropCSSFilter.apply(
  268.           child: child,
  269.           value: BackdropCSSFilterMatrix()
  270.               .sepia(0.35)
  271.               .contrast(1.15)
  272.               .brightness(1.15)
  273.               .saturate(1.8)),
  274.     );
  275.   }

  276.   static Widget insKelvin({required Widget child}) {
  277.     Widget sub = ShaderMask(
  278.         shaderCallback:
  279.             execShaderDirectSample(const Color.fromRGBO(56, 44, 52, 1.0)),
  280.         blendMode: BlendMode.colorDodge,
  281.         child: child);

  282.     return ShaderMask(
  283.       shaderCallback:
  284.           execShaderDirectSample(const Color.fromRGBO(183, 125, 33, 1.0)),
  285.       blendMode: BlendMode.overlay,
  286.       child: sub,
  287.     );
  288.   }

  289.   static Widget insLark({required Widget child}) {
  290.     return BackdropCSSFilter.apply(
  291.         child: child,
  292.         value: BackdropCSSFilterMatrix()
  293.             .sepia(0.25)
  294.             .contrast(1.2)
  295.             .brightness(1.3)
  296.             .saturate(1.25));
  297.   }

  298.   static Widget insLofi({required Widget child}) {
  299.     return BackdropCSSFilter.apply(
  300.         child: child, value: BackdropCSSFilterMatrix().saturate(1.1).contrast(1.5));
  301.   }

  302.   static Widget insLudwig({required Widget child}) {
  303.     return ShaderMask(
  304.       shaderCallback:
  305.           execShaderDirectSample(const Color.fromRGBO(125, 105, 24, 0.1)),
  306.       blendMode: BlendMode.overlay,
  307.       child: BackdropCSSFilter.apply(
  308.           child: child,
  309.           value: BackdropCSSFilterMatrix()
  310.               .sepia(0.25)
  311.               .contrast(1.05)
  312.               .brightness(1.05)
  313.               .saturate(2.0)),
  314.     );
  315.   }

  316.   static Widget insMaven({required Widget child}) {
  317.     return ShaderMask(
  318.       shaderCallback:
  319.           execShaderDirectSample(const Color.fromRGBO(3, 230, 26, 0.2)),
  320.       blendMode: BlendMode.hue,
  321.       child: BackdropCSSFilter.apply(
  322.           child: child,
  323.           value: BackdropCSSFilterMatrix()
  324.               .contrast(0.95)
  325.               .brightness(0.95)
  326.               .saturate(1.5)
  327.               .sepia(0.25)),
  328.     );
  329.   }

  330.   static Widget insMayfair({required Widget child}) {
  331.     return ShaderMask(
  332.       shaderCallback: execShaderRadialSample([
  333.         const Color.fromRGBO(255, 255, 255, 0.32),
  334.         const Color.fromRGBO(255, 200, 200, 0.24),
  335.         const Color.fromRGBO(17, 17, 17, 0.4)
  336.       ], const [
  337.         0.0,
  338.         0.0,
  339.         0.6
  340.       ]),
  341.       blendMode: BlendMode.overlay,
  342.       child: BackdropCSSFilter.apply(
  343.           child: child, value: BackdropCSSFilterMatrix().contrast(1.1).saturate(1.1)),
  344.     );
  345.   }

  346.   static Widget insMoon({required Widget child}) {
  347.     Widget sub = ShaderMask(
  348.       shaderCallback:
  349.           execShaderDirectSample(const Color.fromRGBO(160, 160, 160, 1.0)),
  350.       blendMode: BlendMode.softLight,
  351.       child: BackdropCSSFilter.apply(
  352.           child: child,
  353.           value: BackdropCSSFilterMatrix().grayscale(1).contrast(1.1).brightness(1.1)),
  354.     );

  355.     return ShaderMask(
  356.       shaderCallback:
  357.           execShaderDirectSample(const Color.fromRGBO(56, 56, 56, 1.0)),
  358.       blendMode: BlendMode.lighten,
  359.       child: sub,
  360.     );
  361.   }

  362.   static Widget insMoonV2({required Widget child}) {
  363.     return BackdropCSSFilter.apply(
  364.         child: child,
  365.         value: BackdropCSSFilterMatrix()
  366.             .brightness(1.4)
  367.             .contrast(0.95)
  368.             .saturate(0.0)
  369.             .sepia(0.35));
  370.   }

  371.   static Widget insNashville({required Widget child}) {
  372.     Widget sub = ShaderMask(
  373.       shaderCallback:
  374.           execShaderDirectSample(const Color.fromRGBO(247, 176, 153, 0.56)),
  375.       blendMode: BlendMode.darken,
  376.       child: BackdropCSSFilter.apply(
  377.           child: child,
  378.           value: BackdropCSSFilterMatrix()
  379.               .sepia(0.2)
  380.               .contrast(1.2)
  381.               .brightness(1.05)
  382.               .saturate(1.2)),
  383.     );

  384.     return ShaderMask(
  385.       shaderCallback:
  386.           execShaderDirectSample(const Color.fromRGBO(0, 70, 150, 0.4)),
  387.       blendMode: BlendMode.lighten,
  388.       child: sub,
  389.     );
  390.   }

  391.   static Widget insNashvilleV2({required Widget child}) {
  392.     return ShaderMask(
  393.       shaderCallback: execShaderRadialSample([
  394.         const Color.fromRGBO(128, 78, 15, 0.5),
  395.         const Color.fromRGBO(128, 78, 15, 0.65)
  396.       ]),
  397.       blendMode: BlendMode.screen,
  398.       child: BackdropCSSFilter.apply(
  399.           child: child,
  400.           value: BackdropCSSFilterMatrix()
  401.               .sepia(0.25)
  402.               .contrast(1.5)
  403.               .brightness(0.9)
  404.               .hueRotate(-15.0)),
  405.     );
  406.   }

  407.   static Widget insPerpetua({required Widget child}) {
  408.     return ShaderMask(
  409.       shaderCallback: execShaderLinearSample([
  410.         const Color.fromRGBO(0, 91, 154, 0.5),
  411.         const Color.fromRGBO(230, 193, 61, 0.5)
  412.       ], Alignment.bottomCenter),
  413.       blendMode: BlendMode.softLight,
  414.       child: child,
  415.     );
  416.   }

  417.   static Widget insPoprocket({required Widget child}) {
  418.     return ShaderMask(
  419.       shaderCallback: execShaderRadialSample([
  420.         const Color.fromRGBO(206, 39, 70, 0.75),
  421.         const Color.fromRGBO(0, 0, 0, 1.0)
  422.       ], const [
  423.         0.4,
  424.         0.8
  425.       ]),
  426.       blendMode: BlendMode.screen,
  427.       child: BackdropCSSFilter.apply(
  428.           child: child, value: BackdropCSSFilterMatrix().sepia(0.15).brightness(1.2)),
  429.     );
  430.   }

  431.   static Widget insReyes({required Widget child}) {
  432.     return BackdropCSSFilter.apply(
  433.         child: child,
  434.         value: BackdropCSSFilterMatrix()
  435.             .sepia(0.75)
  436.             .contrast(0.75)
  437.             .brightness(1.25)
  438.             .saturate(1.4));
  439.   }

  440.   static Widget insRise({required Widget child}) {
  441.     Widget sub = ShaderMask(
  442.       shaderCallback: execShaderRadialSample([
  443.         const Color.fromRGBO(236, 205, 169, 0.15),
  444.         const Color.fromRGBO(50, 30, 7, 0.4)
  445.       ], [
  446.         0.55,
  447.         1.0
  448.       ]),
  449.       blendMode: BlendMode.multiply,
  450.       child: BackdropCSSFilter.apply(
  451.           child: child,
  452.           value: BackdropCSSFilterMatrix()
  453.               .brightness(1.05)
  454.               .sepia(0.2)
  455.               .contrast(0.9)
  456.               .saturate(0.9)),
  457.     );

  458.     return ShaderMask(
  459.       shaderCallback: execShaderRadialSample([
  460.         const Color.fromRGBO(232, 197, 152, 0.48),
  461.         const Color.fromRGBO(0, 0, 0, 0.0)
  462.       ], [
  463.         0.0,
  464.         0.9
  465.       ]),
  466.       blendMode: BlendMode.overlay,
  467.       child: sub,
  468.     );
  469.   }

  470.   static Widget insSierra({required Widget child}) {
  471.     return ShaderMask(
  472.       shaderCallback: execShaderRadialSample([
  473.         const Color.fromRGBO(128, 78, 15, 0.5),
  474.         const Color.fromRGBO(0, 0, 0, 0.65)
  475.       ]),
  476.       blendMode: BlendMode.screen,
  477.       child: BackdropCSSFilter.apply(
  478.           child: child,
  479.           value: BackdropCSSFilterMatrix()
  480.               .sepia(0.25)
  481.               .contrast(1.5)
  482.               .brightness(0.9)
  483.               .hueRotate(-15.0)),
  484.     );
  485.   }

  486.   static Widget insSkyline({required Widget child}) {
  487.     return BackdropCSSFilter.apply(
  488.         child: child,
  489.         value: BackdropCSSFilterMatrix()
  490.             .sepia(0.15)
  491.             .contrast(1.25)
  492.             .brightness(1.25)
  493.             .saturate(1.2));
  494.   }

  495.   static Widget insSlumber({required Widget child}) {
  496.     Widget sub = ShaderMask(
  497.       shaderCallback:
  498.           execShaderDirectSample(const Color.fromRGBO(69, 41, 12, 0.4)),
  499.       blendMode: BlendMode.lighten,
  500.       child: BackdropCSSFilter.apply(
  501.           child: child,
  502.           value: BackdropCSSFilterMatrix().saturate(0.66).brightness(1.05)),
  503.     );

  504.     return ShaderMask(
  505.         shaderCallback:
  506.             execShaderDirectSample(const Color.fromRGBO(125, 105, 24, 0.5)),
  507.         blendMode: BlendMode.softLight,
  508.         child: sub);
  509.   }

  510.   static Widget insStinson({required Widget child}) {
  511.     return ShaderMask(
  512.       shaderCallback:
  513.           execShaderDirectSample(const Color.fromRGBO(240, 149, 128, 0.2)),
  514.       blendMode: BlendMode.softLight,
  515.       child: BackdropCSSFilter.apply(
  516.           child: child,
  517.           value:
  518.           BackdropCSSFilterMatrix().contrast(0.75).saturate(0.85).brightness(1.15)),
  519.     );
  520.   }

  521.   static Widget insSutro({required Widget child}) {
  522.     return ShaderMask(
  523.       shaderCallback: execShaderRadialSample([
  524.         const Color.fromRGBO(0, 0, 0, 0.0),
  525.         const Color.fromRGBO(0, 0, 0, 0.5)
  526.       ], const [
  527.         0.5,
  528.         0.9
  529.       ]),
  530.       blendMode: BlendMode.darken,
  531.       child: BackdropCSSFilter.apply(
  532.           child: child,
  533.           value: BackdropCSSFilterMatrix()
  534.               .sepia(0.4)
  535.               .contrast(1.2)
  536.               .brightness(0.9)
  537.               .saturate(1.4)
  538.               .hueRotate(-10.0)),
  539.     );
  540.   }

  541.   static Widget insToaster({required Widget child}) {
  542.     return ShaderMask(
  543.       shaderCallback: execShaderRadialSample([
  544.         const Color.fromRGBO(128, 78, 15, 1.0),
  545.         const Color.fromRGBO(59, 0, 59, 1.0)
  546.       ]),
  547.       blendMode: BlendMode.screen,
  548.       child: BackdropCSSFilter.apply(
  549.           child: child, value: BackdropCSSFilterMatrix().contrast(1.3).brightness(0.9)),
  550.     );
  551.   }

  552.   static Widget insToasterV2({required Widget child}) {
  553.     return ShaderMask(
  554.       shaderCallback: execShaderRadialSample([
  555.         const Color.fromRGBO(128, 78, 15, 1.0),
  556.         const Color.fromRGBO(0, 0, 0, 0.25)
  557.       ]),
  558.       blendMode: BlendMode.screen,
  559.       child: BackdropCSSFilter.apply(
  560.           child: child,
  561.           value: BackdropCSSFilterMatrix()
  562.               .sepia(0.25)
  563.               .contrast(1.5)
  564.               .brightness(0.95)
  565.               .hueRotate(-15.0)),
  566.     );
  567.   }

  568.   static Widget insValencia({required Widget child}) {
  569.     return ShaderMask(
  570.       shaderCallback:
  571.           execShaderDirectSample(const Color.fromRGBO(58, 3, 57, 0.5)),
  572.       blendMode: BlendMode.exclusion,
  573.       child: BackdropCSSFilter.apply(
  574.           child: child,
  575.           value: BackdropCSSFilterMatrix().contrast(1.08).brightness(1.08).sepia(0.08)),
  576.     );
  577.   }

  578.   static Widget insVesper({required Widget child}) {
  579.     return ShaderMask(
  580.       shaderCallback:
  581.           execShaderDirectSample(const Color.fromRGBO(125, 105, 24, 0.25)),
  582.       blendMode: BlendMode.overlay,
  583.       child: BackdropCSSFilter.apply(
  584.           child: child,
  585.           value: BackdropCSSFilterMatrix()
  586.               .sepia(0.35)
  587.               .contrast(1.15)
  588.               .brightness(1.2)
  589.               .saturate(1.3)),
  590.     );
  591.   }

  592.   static Widget insWalden({required Widget child}) {
  593.     return ShaderMask(
  594.       shaderCallback:
  595.           execShaderDirectSample(const Color.fromRGBO(0, 68, 204, 0.3)),
  596.       blendMode: BlendMode.screen,
  597.       child: BackdropCSSFilter.apply(
  598.           child: child,
  599.           value: BackdropCSSFilterMatrix()
  600.               .brightness(1.1)
  601.               .hueRotate(-10.0)
  602.               .sepia(0.3)
  603.               .saturate(1.6)),
  604.     );
  605.   }

  606.   static Widget insWaldenV2({required Widget child}) {
  607.     return ShaderMask(
  608.       shaderCallback:
  609.           execShaderDirectSample(const Color.fromRGBO(229, 240, 128, 0.5)),
  610.       blendMode: BlendMode.darken,
  611.       child: BackdropCSSFilter.apply(
  612.           child: child,
  613.           value: BackdropCSSFilterMatrix()
  614.               .sepia(0.35)
  615.               .contrast(0.8)
  616.               .brightness(1.25)
  617.               .saturate(1.4)),
  618.     );
  619.   }

  620.   static Widget insWillow({required Widget child}) {
  621.     return BackdropCSSFilter.apply(
  622.         child: child,
  623.         value: BackdropCSSFilterMatrix()
  624.             .brightness(1.2)
  625.             .contrast(0.85)
  626.             .saturate(0.05)
  627.             .sepia(0.2));
  628.   }

  629.   static Widget insXpro2({required Widget child}) {
  630.     return ShaderMask(
  631.       shaderCallback: execShaderRadialSample([
  632.         const Color.fromRGBO(230, 231, 224, 1.0),
  633.         const Color.fromRGBO(43, 42, 161, 0.6)
  634.       ], [
  635.         0.4,
  636.         1.1
  637.       ]),
  638.       blendMode: BlendMode.colorBurn,
  639.       child: BackdropCSSFilter.apply(child: child, value: BackdropCSSFilterMatrix().sepia(0.3)),
  640.     );
  641.   }
  642. }
复制代码
utils.dart
  1. import 'package:flutter/material.dart';

  2. List<double> baseMatrix() {
  3.   return <double>[
  4.     1,
  5.     0,
  6.     0,
  7.     0,
  8.     0,
  9.     0,
  10.     1,
  11.     0,
  12.     0,
  13.     0,
  14.     0,
  15.     0,
  16.     1,
  17.     0,
  18.     0,
  19.     0,
  20.     0,
  21.     0,
  22.     1,
  23.     0,
  24.     0,
  25.     0,
  26.     0,
  27.     0,
  28.     1
  29.   ];
  30. }

  31. /// Check: https://github.com/openkraken/kraken/blob/main/kraken/lib/src/css/filter.dart
  32. /// Calc 5x5 matrix multiplication.
  33. List<double> multiplyMatrix5(List<double> a, List<double> b) {
  34.   if (a.length != b.length) {
  35.     throw FlutterError('Matrix length should be same.');
  36.   }

  37.   if (a.length != 25) {
  38.     throw FlutterError('Matrix5 size is not correct.');
  39.   }

  40.   var a00 = a[0];
  41.   var a01 = a[1];
  42.   var a02 = a[2];
  43.   var a03 = a[3];
  44.   var a04 = a[4];
  45.   var a10 = a[5];
  46.   var a11 = a[6];
  47.   var a12 = a[7];
  48.   var a13 = a[8];
  49.   var a14 = a[9];
  50.   var a20 = a[10];
  51.   var a21 = a[11];
  52.   var a22 = a[12];
  53.   var a23 = a[13];
  54.   var a24 = a[14];
  55.   var a30 = a[15];
  56.   var a31 = a[16];
  57.   var a32 = a[17];
  58.   var a33 = a[18];
  59.   var a34 = a[19];
  60.   var a40 = a[20];
  61.   var a41 = a[21];
  62.   var a42 = a[22];
  63.   var a43 = a[23];
  64.   var a44 = a[24];

  65.   var b00 = b[0];
  66.   var b01 = b[1];
  67.   var b02 = b[2];
  68.   var b03 = b[3];
  69.   var b04 = b[4];
  70.   var b10 = b[5];
  71.   var b11 = b[6];
  72.   var b12 = b[7];
  73.   var b13 = b[8];
  74.   var b14 = b[9];
  75.   var b20 = b[10];
  76.   var b21 = b[11];
  77.   var b22 = b[12];
  78.   var b23 = b[13];
  79.   var b24 = b[14];
  80.   var b30 = b[15];
  81.   var b31 = b[16];
  82.   var b32 = b[17];
  83.   var b33 = b[18];
  84.   var b34 = b[19];
  85.   var b40 = b[20];
  86.   var b41 = b[21];
  87.   var b42 = b[22];
  88.   var b43 = b[23];
  89.   var b44 = b[24];

  90.   return [
  91.     a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30 + a04 * b40,
  92.     a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31 + a04 * b41,
  93.     a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32 + a04 * b42,
  94.     a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33 + a04 * b43,
  95.     a00 * b04 + a01 * b14 + a02 * b24 + a03 * b34 + a04 * b44,
  96.     a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30 + a14 * b40,
  97.     a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41,
  98.     a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42,
  99.     a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43,
  100.     a10 * b04 + a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44,
  101.     a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30 + a24 * b40,
  102.     a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41,
  103.     a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42,
  104.     a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43,
  105.     a20 * b04 + a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44,
  106.     a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30 + a34 * b40,
  107.     a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41,
  108.     a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42,
  109.     a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43,
  110.     a30 * b04 + a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44,
  111.     a40 * b00 + a41 * b10 + a42 * b20 + a43 * b30 + a44 * b40,
  112.     a40 * b01 + a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41,
  113.     a40 * b02 + a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42,
  114.     a40 * b03 + a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43,
  115.     a40 * b04 + a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44,
  116.   ];
  117. }

  118. ColorFilter toColorFilterMatrix(List<double> matrix) {
  119.   return ColorFilter.matrix(matrix.sublist(0, 20));
  120. }

  121. Widget execFilterSample(List<double> matrix, Widget child) {
  122.   return ClipRRect(
  123.       child: BackdropFilter(filter: toColorFilterMatrix(matrix), child: child));
  124. }

  125. bool isNotNegative(double v) {
  126.   return v > 0.0;
  127. }

  128. bool isNotDefault(double v) {
  129.   return v != 1.0 && v >= 0.0;
  130. }

  131. execShaderDirectSample(Color color, [Alignment end = Alignment.centerRight]) {
  132.   return (Rect bounds) {
  133.     return LinearGradient(
  134.         end: end,
  135.         colors: [color, color],
  136.         stops: const [0.0, 1.0]).createShader(bounds);
  137.   };
  138. }

  139. execShaderLinearSample(List<Color> colors,
  140.     [Alignment end = Alignment.centerRight,
  141.     List<double> stops = const [0.0, 1.0]]) {
  142.   return (Rect bounds) {
  143.     return LinearGradient(end: end, colors: colors, stops: stops)
  144.         .createShader(bounds);
  145.   };
  146. }

  147. execShaderRadialSample(List<Color> colors,
  148.     [List<double> stops = const [0.0, 1.0], radius = 0.8]) {
  149.   return (Rect bounds) {
  150.     return RadialGradient(
  151.             center: Alignment.center,
  152.             radius: radius,
  153.             colors: colors,
  154.             stops: stops
  155.             // tileMode: TileMode.mirror,
  156.             )
  157.         .createShader(bounds);
  158.   };
  159. }

  160. /// Generates the configuration for applying CSSFilter effects, which is provided to `CSSFilter.apply` for use.
  161. /// Supports chain calls.
  162. ///
  163. /// Example:
  164. ///
  165. /// ```dart
  166. /// CSSFilter.apply(
  167. ///   child: const Text('Hello World!'),
  168. ///   value: CSSFilterMatrix().contrast(1.5).sepia(0.4)
  169. /// );
  170. /// ```
  171. class BackdropCSSFilterMatrix {
  172.   Map conf = {};

  173.   BackdropCSSFilterMatrix contrast([double value = 1.0]) {
  174.     conf['contrast'] = value;
  175.     return this;
  176.   }

  177.   BackdropCSSFilterMatrix grayscale([double value = 0.0]) {
  178.     conf['grayscale'] = value;
  179.     return this;
  180.   }

  181.   BackdropCSSFilterMatrix sepia([double value = 0.0]) {
  182.     conf['sepia'] = value;
  183.     return this;
  184.   }

  185.   BackdropCSSFilterMatrix hueRotate([double value = 0.0]) {
  186.     conf['hueRotate'] = value;
  187.     return this;
  188.   }

  189.   BackdropCSSFilterMatrix brightness([double value = 1.0]) {
  190.     conf['brightness'] = value;
  191.     return this;
  192.   }

  193.   BackdropCSSFilterMatrix saturate([double value = 1.0]) {
  194.     conf['saturate'] = value;
  195.     return this;
  196.   }

  197.   BackdropCSSFilterMatrix invert([double value = 0.0]) {
  198.     conf['invert'] = value;
  199.     return this;
  200.   }

  201.   BackdropCSSFilterMatrix blur([double value = 0.0]) {
  202.     conf['blur'] = value;
  203.     return this;
  204.   }

  205.   BackdropCSSFilterMatrix opacity([double value = 1.0]) {
  206.     conf['opacity'] = value;
  207.     return this;
  208.   }
  209. }
复制代码
至此封装完毕使用方法
  1. BackdropCSSFilter.blur(
  2.                   value: 10,
  3.                   child: BrnAppBar( ... )   
  4.                 ),
复制代码
效果同理
11.webp


去除底部小白条

去除白条后的效果
12.webp

以上就是基于Flutter实现动态高斯模糊的流程步骤的详细内容,更多关于Flutter动态高斯模糊的资料请关注晓枫资讯其它相关文章!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
12
积分
4
注册时间
2023-4-30
最后登录
2023-4-30

发表于 2024-9-9 11:59:00 | 显示全部楼层
感谢楼主,顶。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
16
积分
12
注册时间
2022-12-27
最后登录
2022-12-27

发表于 2024-10-12 02:54:26 | 显示全部楼层
路过,支持一下
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

  • 打卡等级:无名新人
  • 打卡总天数:1
  • 打卡月天数:0
  • 打卡总奖励:9
  • 最近打卡:2024-01-14 16:23:50
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
23
积分
8
注册时间
2023-10-27
最后登录
2024-1-14

发表于 2025-2-28 20:29:20 | 显示全部楼层
感谢楼主分享。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼
3楼
4楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表