permission_dialog_shell.dart 5.25 KB
import 'dart:math' as math;

import 'package:flutter/material.dart';

/// 弹窗标题中使用的应用名称
const String permissionDialogAppName = '班小二';

/// 去掉「权限」后缀,得到归一化后的权限名称(兼容「相机」与「相机权限」两种写法)
String normalizePermissionName(String permission) {
  return permission.endsWith('权限') ? permission.substring(0, permission.length - 2) : permission;
}

/// 标题句式中与默认写法不同的权限(相册为「访问」,其余为「使用」)
const Map<String, String> _titleSuffixes = {
  '相册': '需要访问您的相册',
};

/// 权限弹窗标题,例如:「班小二APP」需要使用相机、「班小二APP」需要访问您的相册
String permissionDialogTitle(String permission) {
  final name = normalizePermissionName(permission);
  final suffix = _titleSuffixes[name] ?? '需要使用$name';
  return '"${permissionDialogAppName}APP"$suffix';
}

/// 权限类弹窗的纯样式骨架
///
/// 只负责渐变背景、盾牌插画、标题、说明以及主次按钮的排版与视觉,
/// 具体的文案与按钮行为由各业务组件通过参数传入,
/// 以便「首次使用权限说明」与「权限被拒后去设置引导」保持视觉统一。
class PermissionDialogShell extends StatelessWidget {
  const PermissionDialogShell({
    super.key,
    required this.title,
    required this.description,
    required this.primaryText,
    required this.onPrimary,
    required this.secondaryText,
    required this.onSecondary,
  });

  /// 弹窗标题,例如:「班小二APP」需要使用相机
  final String title;

  /// 弹窗说明文案
  final String description;

  /// 主按钮文案,例如:继续
  final String primaryText;

  /// 主按钮点击回调
  final VoidCallback onPrimary;

  /// 次按钮文案,例如:暂不开启
  final String secondaryText;

  /// 次按钮点击回调
  final VoidCallback onSecondary;

  /// 盾牌插画资源
  static const String shieldAsset = 'assets/images/icon/shield.png';

  @override
  Widget build(BuildContext context) {
    return Dialog(
      backgroundColor: Colors.transparent,
      elevation: 0,
      insetPadding: const EdgeInsets.symmetric(horizontal: 40),
      child: Container(
        padding: const EdgeInsets.fromLTRB(22, 0, 22, 10),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(20),
          gradient: const LinearGradient(
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            colors: [
              Color(0xFFBAC8FC),
              Color(0xFFFFFFFF),
            ],
            stops: [0, 0.25],
            transform: GradientRotation(20 * math.pi / 180),
          ),
        ),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // 插画
            Center(
              child: Image.asset(
                shieldAsset,
                height: 100,
                fit: BoxFit.contain,
              ),
            ),
            const SizedBox(height: 16),
            // 标题
            Text(
              title,
              textAlign: TextAlign.center,
              style: const TextStyle(
                fontSize: 17,
                fontWeight: FontWeight.w600,
                height: 1.4,
                color: Color(0xFF1A1A1A),
              ),
            ),
            const SizedBox(height: 10),
            // 说明
            Text(
              description,
              textAlign: TextAlign.left,
              style: const TextStyle(
                fontSize: 15,
                height: 1.6,
                color: Color(0xFF333333),
              ),
            ),
            const SizedBox(height: 22),
            // 主按钮
            ClipRRect(
              borderRadius: BorderRadius.circular(23),
              child: Material(
                color: Colors.transparent,
                child: Ink(
                  decoration: const BoxDecoration(
                    color: Color(0xFF7691FA),
                  ),
                  child: InkWell(
                    onTap: onPrimary,
                    child: SizedBox(
                      height: 46,
                      child: Center(
                        child: Text(
                          primaryText,
                          style: const TextStyle(
                            fontSize: 16,
                            fontWeight: FontWeight.w500,
                            color: Colors.white,
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            ),
            const SizedBox(height: 8),
            // 次按钮
            TextButton(
              onPressed: onSecondary,
              style: TextButton.styleFrom(
                minimumSize: const Size.fromHeight(40),
                foregroundColor: const Color(0xFF999999),
              ),
              child: Text(
                secondaryText,
                style: const TextStyle(
                  fontSize: 16,
                  color: Color(0xFF999999),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}