Flutter:导航固定背景图,滚动时导航颜色渐变



view

js 复制代码
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
import 'package:happy/common/index.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';

import 'index.dart';

class NoticeDetailPage extends GetView<NoticeDetailController> {
  const NoticeDetailPage({super.key});

  // 头部
  Widget _buildHeader() {
    return <Widget>[
      TextWidget.body(
        controller.title,
        size: 28.sp,
        weight: FontWeight.w600,
        color: AppTheme.color000,
      ),
      SizedBox(
        height: 20.w,
      ),
      <Widget>[
        Icon(
          Icons.access_time_outlined,
          size: 28.sp,
          color: AppTheme.color000,
        ),
        SizedBox(
          width: 10.w,
        ),
        TextWidget.body(
          controller.time,
          color: AppTheme.color666,
        ),
      ].toRow(mainAxisAlignment: MainAxisAlignment.center)
    ]
        .toColumn(crossAxisAlignment: CrossAxisAlignment.center)
        .paddingOnly(top: 20.w, bottom: 20.w)
        .card(color: AppTheme.dividerColor2);
  }
  // 内容详情
  Widget _buildContent() {
    return <Widget>[
      HtmlWidget(
        controller.content,
        // 设置渲染模式
        renderMode: RenderMode.column,
        // 设置文本样式
        textStyle: TextStyle(
          fontSize: 28.sp,
          color: Colors.white,
          height: 1.5,
        ),
      ),
    ]
        .toColumn(crossAxisAlignment: CrossAxisAlignment.center)
        .paddingAll(30.w)
        .card(color: AppTheme.dividerColor2);
  }

  // 主视图
  Widget _buildView() {
    // 系统状态栏占位高度
    double systemStatusBarHeight = MediaQuery.of(Get.context!).padding.top;
    // 总占位高度
    double systemTotalHeight = 44 + systemStatusBarHeight;
    return SingleChildScrollView(
      controller: controller.scrollController,
      child: <Widget>[
        SizedBox(height: systemTotalHeight,),
        _buildHeader(),
        SizedBox(height: 20.w,),
        _buildContent(),
      ].toColumn(crossAxisAlignment: CrossAxisAlignment.start).paddingHorizontal(30.w),
    );
  }

  @override
  Widget build(BuildContext context) {
    return GetBuilder<NoticeDetailController>(
      init: NoticeDetailController(),
      id: "notice_detail",
      builder: (_) {
        return Scaffold(
          backgroundColor: AppTheme.pageBgColor,
          body: <Widget>[
            ImgWidget(path: 'assets/images/home11.png',width: 750.w,height: 500.w,),
            _buildView().expanded(),
            // 只刷新导航栏
            GetBuilder<NoticeDetailController>(
              id: "notice_detail_navbar",
              builder: (_) => AnimatedContainer(
                duration: const Duration(milliseconds: 100),
                color: Colors.white.withOpacity(controller.opacity),
                child: TDNavBar(
                  height: 44,
                  title: '消息详情',
                  titleColor: AppTheme.color000,
                  titleFontWeight: FontWeight.w600,
                  backgroundColor: Colors.transparent,
                  screenAdaptation: true,
                  useDefaultBack: false,
                  leftBarItems: [
                    TDNavBarItem(
                      icon: TDIcons.chevron_left,
                      iconSize: 24,
                      iconColor: AppTheme.color000,
                      action: () {
                        Get.back();
                      }
                    ),
                  ],
                ),
              ),
            ),
          ].toStack(),
        );
      },
    );
  }
}

controller

js 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:happy/common/index.dart';

class NoticeDetailController extends GetxController {
  NoticeDetailController();
  // 滚动控制器
  final ScrollController scrollController = ScrollController();
  
  // 渐变系数 0-1
  double opacity = 0.0;
  
  // 滚动开始变化的位置
  final double scrollStartPoint = 20.0;
  
  // 滚动结束变化的位置
  final double scrollEndPoint = 120.0;

  
  int id = 0;
  String title = '';
  String content = '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试';
  String time = '';


  _initData() async {
    id = Get.arguments['id'];
    var res = await HomeApi.noticeDetail(id);
    content = res.content ?? '';
    title = res.title ?? '';
    time = res.updatedAt ?? '';
    update(["notice_detail"]);
  }


  @override
  void onReady() {
    super.onReady();
    _initData();
  }

  @override
  void onInit() {
    super.onInit();
    // 监听滚动
    scrollController.addListener(() {
      // 计算 0-1 之间的渐变系数
      double newOpacity;
      
      if (scrollController.offset <= scrollStartPoint) {
        // 开始点之前完全透明
        newOpacity = 0.0;
      } else if (scrollController.offset >= scrollEndPoint) {
        // 结束点之后完全不透明
        newOpacity = 1.0;
      } else {
        // 在开始点和结束点之间线性计算
        newOpacity = (scrollController.offset - scrollStartPoint) /  (scrollEndPoint - scrollStartPoint);
        
        // 确保值在0-1范围内并保留更多小数位精度
        newOpacity = double.parse(newOpacity.toStringAsFixed(3)).clamp(0.0, 1.0);
      }
      
      // 只有当透明度变化时才更新UI
      if ((opacity - newOpacity).abs() > 0.001) {
        opacity = newOpacity;
        update(["notice_detail_navbar"]);
      }
    });
    _initData();
  }

  @override
  void onClose() {
    scrollController.dispose();
    super.onClose();
  }
}
相关推荐
2501_9209317019 小时前
React Native鸿蒙跨平台采用ScrollView的horizontal属性实现横向滚动实现特色游戏轮播和分类导航
javascript·react native·react.js·游戏·ecmascript·harmonyos
向哆哆21 小时前
打造高校四六级报名管理系统:基于 Flutter × OpenHarmony 的跨端开发实践
flutter·开源·鸿蒙·openharmony·开源鸿蒙
2501_9400078921 小时前
Flutter for OpenHarmony三国杀攻略App实战 - 设置功能实现
flutter
东东51621 小时前
智能社区管理系统的设计与实现ssm+vue
前端·javascript·vue.js·毕业设计·毕设
catino21 小时前
图片、文件的预览
前端·javascript
lbb 小魔仙1 天前
【Harmonyos】开源鸿蒙跨平台训练营DAY9:获取分类数据并渲染
flutter·华为·harmonyos
mocoding1 天前
Flutter 3D 翻转动画flip_card三方库在鸿蒙版天气预报卡片中的实战教程
flutter·3d·harmonyos
2501_920931701 天前
React Native鸿蒙跨平台实现推箱子游戏,完成玩家移动与箱子推动,当所有箱子都被推到目标位置时,玩家获胜
javascript·react native·react.js·游戏·ecmascript·harmonyos
AI老李1 天前
PostCSS完全指南:功能/配置/插件/SourceMap/AST/插件开发/自定义语法
前端·javascript·postcss
JMchen1231 天前
现代Android图像处理管道:从CameraX到OpenGL的60fps实时滤镜架构
android·图像处理·架构·kotlin·android studio·opengl·camerax