🚀 示例应用+效果图在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

void main() => runApp(const ImageRadiusDemoApp());

/// Image Widget 图片处理演示应用
class ImageRadiusDemoApp extends StatelessWidget {
  const ImageRadiusDemoApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Image Widget 图片处理演示',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
          brightness: Brightness.light,
        ),
      ),
      home: const HomePage(),
    );
  }
}

/// 主页面
class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[100],
      appBar: AppBar(
        title: const Text(
          'Image Widget 图片处理演示',
          style: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
        centerTitle: true,
        elevation: 0,
        backgroundColor: Colors.blue[600],
      ),
      body: const SingleChildScrollView(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            RoundedImageCard(),
            SizedBox(height: 20),
            ImageClipCard(),
            SizedBox(height: 20),
            ShadowEffectCard(),
            SizedBox(height: 20),
            CombinedEffectCard(),
            SizedBox(height: 20),
            ProductCard(),
            SizedBox(height: 20),
            UserListCard(),
            SizedBox(height: 20),
            RealEstateCard(),
          ],
        ),
      ),
    );
  }
}

/// 圆角图片卡片
class RoundedImageCard extends StatelessWidget {
  const RoundedImageCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.08),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              '🖼️ 圆角图片效果',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.grey[800],
              ),
            ),
          ),
          const Divider(height: 1),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    _buildRoundedImage(8, '圆角8px'),
                    _buildRoundedImage(16, '圆角16px'),
                    _buildRoundedImage(32, '圆角32px'),
                  ],
                ),
                const SizedBox(height: 16),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    _buildRoundedImage(100, '圆形'),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildRoundedImage(double radius, String label) {
    return Column(
      children: [
        ClipRRect(
          borderRadius: BorderRadius.circular(radius),
          child: Image.network(
            'https://images.unsplash.com/photo-1501785888041-af3ef285b470?w=200',
            width: 100,
            height: 100,
            fit: BoxFit.cover,
            errorBuilder: (context, error, stackTrace) {
              return Container(
                width: 100,
                height: 100,
                color: Colors.grey[200],
                child: const Icon(Icons.error, color: Colors.grey),
              );
            },
          ),
        ),
        const SizedBox(height: 8),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey[600],
          ),
        ),
      ],
    );
  }
}

/// 图片裁剪展示
class ImageClipCard extends StatelessWidget {
  const ImageClipCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.08),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              '✂️ 图片裁剪效果',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.grey[800],
              ),
            ),
          ),
          const Divider(height: 1),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    _buildClipImage('circle', '圆形裁剪'),
                    _buildClipImage('rect', '矩形裁剪'),
                  ],
                ),
                const SizedBox(height: 16),
                _buildClipImage('custom', '自定义裁剪'),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildClipImage(String type, String label) {
    Widget image = Image.network(
      'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=200',
      width: 100,
      height: 100,
      fit: BoxFit.cover,
      errorBuilder: (context, error, stackTrace) {
        return Container(
          width: 100,
          height: 100,
          color: Colors.grey[200],
          child: const Icon(Icons.error, color: Colors.grey),
        );
      },
    );

    switch (type) {
      case 'circle':
        image = ClipOval(child: image);
        break;
      case 'rect':
        image = ClipRRect(
          borderRadius: BorderRadius.circular(8),
          child: image,
        );
        break;
      case 'custom':
        image = ClipPath(
          clipper: StarClipper(),
          child: image,
        );
        break;
    }

    return Column(
      children: [
        image,
        const SizedBox(height: 8),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey[600],
          ),
        ),
      ],
    );
  }
}

/// 星形裁剪路径
class StarClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    final path = Path();
    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2;
    final points = 5;
    final innerRadius = radius * 0.5;

    for (int i = 0; i < points * 2; i++) {
      final angle = (i * 3.1415926) / points - 3.1415926 / 2;
      final r = i % 2 == 0 ? radius : innerRadius;
      final x = center.dx + r * cos(angle);
      final y = center.dy + r * sin(angle);

      if (i == 0) {
        path.moveTo(x, y);
      } else {
        path.lineTo(x, y);
      }
    }

    path.close();
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

/// 图片阴影效果
class ShadowEffectCard extends StatelessWidget {
  const ShadowEffectCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.08),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              '🌟 图片阴影效果',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.grey[800],
              ),
            ),
          ),
          const Divider(height: 1),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    _buildShadowImage('soft', '轻微阴影'),
                    _buildShadowImage('deep', '深度阴影'),
                  ],
                ),
                const SizedBox(height: 16),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    _buildShadowImage('color', '彩色阴影'),
                    _buildShadowImage('glow', '发光效果'),
                  ],
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildShadowImage(String type, String label) {
    BoxShadow shadow;

    switch (type) {
      case 'soft':
        shadow = BoxShadow(
          color: Colors.black.withOpacity(0.1),
          blurRadius: 8,
          offset: const Offset(0, 2),
        );
        break;
      case 'deep':
        shadow = BoxShadow(
          color: Colors.black.withOpacity(0.3),
          blurRadius: 20,
          offset: const Offset(0, 10),
        );
        break;
      case 'color':
        shadow = BoxShadow(
          color: Colors.blue.withOpacity(0.5),
          blurRadius: 15,
          offset: const Offset(0, 8),
        );
        break;
      case 'glow':
        shadow = BoxShadow(
          color: Colors.amber.withOpacity(0.6),
          blurRadius: 30,
          offset: const Offset(0, 0),
          spreadRadius: 5,
        );
        break;
      default:
        shadow = BoxShadow(
          color: Colors.black.withOpacity(0.1),
          blurRadius: 8,
          offset: const Offset(0, 2),
        );
    }

    return Column(
      children: [
        Container(
          decoration: BoxDecoration(
            boxShadow: [shadow],
          ),
          child: ClipRRect(
            borderRadius: BorderRadius.circular(12),
            child: Image.network(
              'https://images.unsplash.com/photo-1516961642265-531546e84af2?w=200',
              width: 100,
              height: 100,
              fit: BoxFit.cover,
              errorBuilder: (context, error, stackTrace) {
                return Container(
                  width: 100,
                  height: 100,
                  color: Colors.grey[200],
                  child: const Icon(Icons.error, color: Colors.grey),
                );
              },
            ),
          ),
        ),
        const SizedBox(height: 8),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey[600],
          ),
        ),
      ],
    );
  }
}

/// 组合效果展示
class CombinedEffectCard extends StatelessWidget {
  const CombinedEffectCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.08),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              '🎭 组合效果展示',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.grey[800],
              ),
            ),
          ),
          const Divider(height: 1),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                _buildCombinedCard('rounded', '圆角+阴影'),
                _buildCombinedCard('circle', '圆形+阴影'),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildCombinedCard(String type, String label) {
    Widget image = Image.network(
      type == 'rounded'
          ? 'https://images.unsplash.com/photo-1497215728101-856f4ea42174?w=200'
          : 'https://api.dicebear.com/7.x/avataaars/svg?seed=John',
      width: 100,
      height: 100,
      fit: BoxFit.cover,
      errorBuilder: (context, error, stackTrace) {
        return Container(
          width: 100,
          height: 100,
          color: Colors.grey[200],
          child: const Icon(Icons.error, color: Colors.grey),
        );
      },
    );

    if (type == 'rounded') {
      image = ClipRRect(
        borderRadius: BorderRadius.circular(16),
        child: image,
      );
    } else {
      image = ClipOval(child: image);
    }

    return Column(
      children: [
        Container(
          decoration: BoxDecoration(
            boxShadow: [
              BoxShadow(
                color: Colors.black.withOpacity(0.2),
                blurRadius: 12,
                offset: const Offset(0, 4),
              ),
            ],
          ),
          child: image,
        ),
        const SizedBox(height: 8),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey[600],
          ),
        ),
      ],
    );
  }
}

/// 产品卡片
class ProductCard extends StatelessWidget {
  const ProductCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.08),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              '📱 实际应用:产品卡片',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.grey[800],
              ),
            ),
          ),
          const Divider(height: 1),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Container(
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(16),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withOpacity(0.15),
                    blurRadius: 16,
                    offset: const Offset(0, 8),
                  ),
                ],
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  // 产品图片
                  ClipRRect(
                    borderRadius: const BorderRadius.vertical(
                      top: Radius.circular(16),
                    ),
                    child: Image.network(
                      'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600',
                      width: double.infinity,
                      height: 180,
                      fit: BoxFit.cover,
                      errorBuilder: (context, error, stackTrace) {
                        return Container(
                          width: double.infinity,
                          height: 180,
                          color: Colors.grey[200],
                          child: const Icon(Icons.error, color: Colors.grey),
                        );
                      },
                    ),
                  ),
                  // 产品信息
                  Padding(
                    padding: const EdgeInsets.all(16),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Container(
                              padding: const EdgeInsets.symmetric(
                                horizontal: 8,
                                vertical: 4,
                              ),
                              decoration: BoxDecoration(
                                color: Colors.blue[50],
                                borderRadius: BorderRadius.circular(8),
                              ),
                              child: Text(
                                '耳机',
                                style: TextStyle(
                                  fontSize: 12,
                                  color: Colors.blue[700],
                                  fontWeight: FontWeight.w500,
                                ),
                              ),
                            ),
                            Row(
                              children: [
                                const Icon(
                                  Icons.star,
                                  size: 16,
                                  color: Colors.orange,
                                ),
                                const SizedBox(width: 4),
                                Text(
                                  '4.8',
                                  style: TextStyle(
                                    fontSize: 14,
                                    color: Colors.grey[700],
                                  ),
                                ),
                              ],
                            ),
                          ],
                        ),
                        const SizedBox(height: 12),
                        Text(
                          'Sony WH-1000XM4 无线降噪耳机',
                          style: TextStyle(
                            fontSize: 18,
                            fontWeight: FontWeight.bold,
                            color: Colors.grey[800],
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text(
                          '行业领先的降噪技术,30小时续航,舒适佩戴体验。',
                          style: TextStyle(
                            fontSize: 14,
                            color: Colors.grey[600],
                            height: 1.5,
                          ),
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                        ),
                        const SizedBox(height: 16),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Row(
                                  crossAxisAlignment: CrossAxisAlignment.end,
                                  children: [
                                    Text(
                                      '¥1,999',
                                      style: TextStyle(
                                        fontSize: 24,
                                        fontWeight: FontWeight.bold,
                                        color: Colors.red[600],
                                      ),
                                    ),
                                    const SizedBox(width: 8),
                                    Text(
                                      '¥2,499',
                                      style: TextStyle(
                                        fontSize: 14,
                                        color: Colors.grey[400],
                                        decoration: TextDecoration.lineThrough,
                                      ),
                                    ),
                                  ],
                                ),
                              ],
                            ),
                            ElevatedButton.icon(
                              onPressed: () {},
                              icon: const Icon(Icons.shopping_cart, size: 18),
                              label: const Text('购买'),
                              style: ElevatedButton.styleFrom(
                                backgroundColor: Colors.blue[600],
                                foregroundColor: Colors.white,
                                padding: const EdgeInsets.symmetric(
                                  horizontal: 20,
                                  vertical: 12,
                                ),
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(12),
                                ),
                              ),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

/// 用户列表卡片
class UserListCard extends StatelessWidget {
  const UserListCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.08),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              '👥 实际应用:用户列表',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.grey[800],
              ),
            ),
          ),
          const Divider(height: 1),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                _buildUserItem(
                  'https://api.dicebear.com/7.x/avataaars/svg?seed=Alice',
                  '张三',
                  '前端开发工程师',
                  true,
                  '2分钟前',
                ),
                const SizedBox(height: 12),
                _buildUserItem(
                  'https://api.dicebear.com/7.x/avataaars/svg?seed=Bob',
                  '李四',
                  'UI设计师',
                  false,
                  '15分钟前',
                ),
                const SizedBox(height: 12),
                _buildUserItem(
                  'https://api.dicebear.com/7.x/avataaars/svg?seed=Carol',
                  '王五',
                  '产品经理',
                  true,
                  '1小时前',
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildUserItem(
    String avatarUrl,
    String name,
    String role,
    bool isOnline,
    String time,
  ) {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: Colors.grey[50],
        borderRadius: BorderRadius.circular(12),
      ),
      child: Row(
        children: [
          // 头像
          Stack(
            children: [
              ClipOval(
                child: Image.network(
                  avatarUrl,
                  width: 48,
                  height: 48,
                  fit: BoxFit.cover,
                  errorBuilder: (context, error, stackTrace) {
                    return Container(
                      width: 48,
                      height: 48,
                      color: Colors.grey[200],
                      child: const Icon(Icons.person, color: Colors.grey),
                    );
                  },
                ),
              ),
              // 在线状态指示器
              Positioned(
                right: 0,
                bottom: 0,
                child: Container(
                  width: 14,
                  height: 14,
                  decoration: BoxDecoration(
                    color: isOnline ? Colors.green : Colors.grey,
                    border: Border.all(color: Colors.white, width: 2),
                    shape: BoxShape.circle,
                  ),
                ),
              ),
            ],
          ),
          const SizedBox(width: 12),
          // 用户信息
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  name,
                  style: TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                    color: Colors.grey[800],
                  ),
                ),
                const SizedBox(height: 4),
                Text(
                  role,
                  style: TextStyle(
                    fontSize: 13,
                    color: Colors.grey[600],
                  ),
                ),
              ],
            ),
          ),
          // 时间
          Text(
            time,
            style: TextStyle(
              fontSize: 12,
              color: Colors.grey[400],
            ),
          ),
          const SizedBox(width: 8),
          // 操作按钮
          Icon(Icons.more_vert, color: Colors.grey[400]),
        ],
      ),
    );
  }
}

/// 房产展示卡片
class RealEstateCard extends StatelessWidget {
  const RealEstateCard({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.08),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Text(
              '🏠 实际应用:房产展示',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Colors.grey[800],
              ),
            ),
          ),
          const Divider(height: 1),
          Padding(
            padding: const EdgeInsets.all(16),
            child: Container(
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(16),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withOpacity(0.15),
                    blurRadius: 16,
                    offset: const Offset(0, 8),
                  ),
                ],
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  // 房产图片
                  Stack(
                    children: [
                      ClipRRect(
                        borderRadius: const BorderRadius.vertical(
                          top: Radius.circular(16),
                        ),
                        child: Image.network(
                          'https://images.unsplash.com/photo-1512917774080-9991f1c4c750?w=800',
                          width: double.infinity,
                          height: 200,
                          fit: BoxFit.cover,
                          errorBuilder: (context, error, stackTrace) {
                            return Container(
                              width: double.infinity,
                              height: 200,
                              color: Colors.grey[200],
                              child: const Icon(Icons.error, color: Colors.grey),
                            );
                          },
                        ),
                      ),
                      // 价格标签
                      Positioned(
                        top: 16,
                        left: 16,
                        child: Container(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 12,
                            vertical: 6,
                          ),
                          decoration: BoxDecoration(
                            color: Colors.red[600],
                            borderRadius: BorderRadius.circular(20),
                            boxShadow: [
                              BoxShadow(
                                color: Colors.red.withOpacity(0.3),
                                blurRadius: 8,
                                offset: const Offset(0, 4),
                              ),
                            ],
                          ),
                          child: Text(
                            '¥580万',
                            style: const TextStyle(
                              fontSize: 18,
                              fontWeight: FontWeight.bold,
                              color: Colors.white,
                            ),
                          ),
                        ),
                      ),
                      // 收藏按钮
                      Positioned(
                        top: 16,
                        right: 16,
                        child: Container(
                          decoration: BoxDecoration(
                            color: Colors.white.withOpacity(0.9),
                            shape: BoxShape.circle,
                            boxShadow: [
                              BoxShadow(
                                color: Colors.black.withOpacity(0.1),
                                blurRadius: 8,
                                offset: const Offset(0, 2),
                              ),
                            ],
                          ),
                          child: const Padding(
                            padding: EdgeInsets.all(8),
                            child: Icon(
                              Icons.favorite_border,
                              color: Colors.grey,
                              size: 20,
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                  // 房产信息
                  Padding(
                    padding: const EdgeInsets.all(16),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        // 标题
                        Text(
                          '阳光花园 3室2厅 南北通透',
                          style: TextStyle(
                            fontSize: 18,
                            fontWeight: FontWeight.bold,
                            color: Colors.grey[800],
                          ),
                        ),
                        const SizedBox(height: 8),
                        // 地址
                        Row(
                          children: [
                            Icon(Icons.location_on,
                                size: 16, color: Colors.grey[400]),
                            const SizedBox(width: 4),
                            Text(
                              '北京市朝阳区建国路88号',
                              style: TextStyle(
                                fontSize: 14,
                                color: Colors.grey[600],
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 12),
                        // 标签
                        Wrap(
                          spacing: 8,
                          runSpacing: 8,
                          children: [
                            _buildTag('3室2厅', Colors.blue),
                            _buildTag('138㎡', Colors.green),
                            _buildTag('南北通透', Colors.orange),
                            _buildTag('精装修', Colors.purple),
                          ],
                        ),
                        const SizedBox(height: 16),
                        // 详细信息
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            _buildInfo('面积', '138㎡'),
                            _buildInfo('楼层', '12/26'),
                            _buildInfo('朝向', '南北'),
                            _buildInfo('年代', '2018年'),
                          ],
                        ),
                        const SizedBox(height: 16),
                        // 操作按钮
                        Row(
                          children: [
                            Expanded(
                              child: OutlinedButton.icon(
                                onPressed: () {},
                                icon: const Icon(Icons.phone, size: 18),
                                label: const Text('联系经纪人'),
                                style: OutlinedButton.styleFrom(
                                  foregroundColor: Colors.blue[600],
                                  side: BorderSide(color: Colors.blue[600]!),
                                  padding: const EdgeInsets.symmetric(
                                    vertical: 12,
                                  ),
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(12),
                                  ),
                                ),
                              ),
                            ),
                            const SizedBox(width: 12),
                            Expanded(
                              child: ElevatedButton.icon(
                                onPressed: () {},
                                icon: const Icon(Icons.visibility, size: 18),
                                label: const Text('预约看房'),
                                style: ElevatedButton.styleFrom(
                                  backgroundColor: Colors.blue[600],
                                  foregroundColor: Colors.white,
                                  padding: const EdgeInsets.symmetric(
                                    vertical: 12,
                                  ),
                                  shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(12),
                                  ),
                                ),
                              ),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildTag(String text, Color color) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
      decoration: BoxDecoration(
        color: color.withOpacity(0.1),
        borderRadius: BorderRadius.circular(12),
      ),
      child: Text(
        text,
        style: TextStyle(
          fontSize: 12,
          color: color,
          fontWeight: FontWeight.w500,
        ),
      ),
    );
  }

  Widget _buildInfo(String label, String value) {
    return Column(
      children: [
        Text(
          value,
          style: TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.bold,
            color: Colors.grey[800],
          ),
        ),
        const SizedBox(height: 4),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey[500],
          ),
        ),
      ],
    );
  }
}

运行步骤

# 进入示例项目目录
cd flutter_examples/image_radius_demo

# 运行应用(鸿蒙虚拟机)
flutter run -d 127.0.0.1:5555

# 或运行应用(其他设备)
flutter run

演示内容

运行应用后,您将看到以下7个演示组件:

  1. 🖼️ 圆角图片效果 - 展示不同圆角大小的图片
  2. ✂️ 图片裁剪效果 - 展示圆形、矩形、自定义裁剪
  3. 🌟 图片阴影效果 - 展示轻微、深度、彩色、发光阴影
  4. 🎭 组合效果展示 - 展示圆角+阴影、圆形+阴影组合
  5. 📱 实际应用:产品卡片 - 电商产品展示
  6. 👥 实际应用:用户列表 - 社交用户列表
  7. 🏠 实际应用:房产展示 - 房产信息卡片

每个组件都包含完整的代码实现,可以直接查看和学习。


一、 前言

在上一篇《Image Widget 基础:图片加载方式》中,我们学习了如何加载不同来源的图片。然而,在实际应用中,仅仅显示图片是远远不够的。现代 UI 设计要求图片具有各种视觉效果:圆角、阴影、裁剪等。

本篇文章将深入探讨 Flutter 中 Image Widget 的高级图片处理技术,包括:

  • 使用 ClipRRect 实现圆角效果
  • 使用 ClipOval、ClipPath 实现各种裁剪效果
  • 使用 BoxShadow 添加阴影效果
  • 组合多种效果创建精美的 UI 组件

这些技术不仅适用于图片,也适用于其他 Widget,是 Flutter UI 开发中的重要技能。


二、 图片处理技术架构

2.1 图片处理技术体系

Image Widget

Clip系列组件

Container装饰

组合效果

ClipRRect 圆角

ClipOval 圆形

ClipRect 矩形

ClipPath 自定义

BoxShadow 阴影

BorderRadius 圆角

Border 边框

圆角+阴影

圆形+阴影

自定义组合

2.2 核心组件对比

组件 主要用途 优势 适用场景
ClipRRect 圆角裁剪 灵活控制圆角 卡片、按钮图片
ClipOval 圆形裁剪 简单直接 头像、圆形图标
ClipRect 矩形裁剪 性能最优 简单裁剪场景
ClipPath 自定义裁剪 无限可能 特殊形状、创意设计
BoxShadow 阴影效果 逼真立体 卡片、悬浮按钮

三、 圆角图片处理

3.1 ClipRRect 原理

ClipRRect(Clipped Rounded Rectangle)是 Flutter 中实现圆角效果的核心组件。它通过裁剪子组件的矩形区域,使其四个角呈现圆弧状。

原始矩形

ClipRRect

圆角矩形

3.2 基础用法

ClipRRect(
  borderRadius: BorderRadius.circular(16),  // 圆角半径
  child: Image.asset('assets/image.jpg'),
)
3.3.1 不同圆角效果对比
方法 效果 代码示例
circular(16) 四角相同圆角 BorderRadius.circular(16)
only(topLeft: 16) 仅左上角圆角 BorderRadius.only(topLeft: Radius.circular(16))
vertical(top: 16) 上下圆角 BorderRadius.vertical(top: Radius.circular(16))
horizontal(left: 16) 左右圆角 BorderRadius.horizontal(left: Radius.circular(16))
3.3.2 实际代码示例
// 四角相同圆角
ClipRRect(
  borderRadius: BorderRadius.circular(16),
  child: Image.asset('assets/image.jpg'),
)

// 仅顶部圆角
ClipRRect(
  borderRadius: const BorderRadius.vertical(
    top: Radius.circular(16),
  ),
  child: Image.asset('assets/image.jpg'),
)

// 自定义每个角的圆角
ClipRRect(
  borderRadius: BorderRadius.only(
    topLeft: Radius.circular(20),
    topRight: Radius.circular(10),
    bottomLeft: Radius.circular(10),
    bottomRight: Radius.circular(20),
  ),
  child: Image.asset('assets/image.jpg'),
)

四、 图片裁剪技术

4.1 ClipOval 圆形裁剪

ClipOval 将子组件裁剪为椭圆形或圆形(当宽高相等时)。

// 圆形裁剪(头像)
ClipOval(
  child: Image.network(
    'https://api.dicebear.com/7.x/avataaars/svg?seed=John',
    width: 100,
    height: 100,
    fit: BoxFit.cover,
  ),
)

// 椭圆形裁剪
ClipOval(
  child: Image.network(
    'https://example.com/image.jpg',
    width: 200,
    height: 100,
    fit: BoxFit.cover,
  ),
)

4.2 ClipRect 矩形裁剪

ClipRect 将子组件裁剪为矩形,通常用于裁剪溢出内容。

ClipRect(
  child: Align(
    alignment: Alignment.center,
    heightFactor: 0.5,  // 只显示上半部分
    child: Image.asset('assets/image.jpg'),
  ),
)

4.3 ClipPath 自定义裁剪

ClipPath 允许使用自定义路径进行裁剪,实现任意形状。

4.3.1 自定义 Clipper
class StarClipper extends CustomClipper<Path> {
  
  Path getClip(Size size) {
    final path = Path();
    final center = Offset(size.width / 2, size.height / 2);
    final radius = size.width / 2;
    final points = 5;
    final innerRadius = radius * 0.5;

    for (int i = 0; i < points * 2; i++) {
      final angle = (i * 3.1415926) / points - 3.1415926 / 2;
      final r = i % 2 == 0 ? radius : innerRadius;
      final x = center.dx + r * cos(angle);
      final y = center.dy + r * sin(angle);

      if (i == 0) {
        path.moveTo(x, y);
      } else {
        path.lineTo(x, y);
      }
    }

    path.close();
    return path;
  }

  
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
4.3.2 使用自定义 Clipper
ClipPath(
  clipper: StarClipper(),
  child: Image.asset('assets/image.jpg'),
)

4.4 裁剪效果对比

原始图片

ClipRRect

ClipOval

ClipRect

ClipPath

圆角矩形

圆形/椭圆

矩形裁剪

自定义形状


五、 图片阴影效果

5.1 BoxShadow 核心属性

BoxShadow

+Color color

+double blurRadius

+Offset offset

+double spreadRadius

+BlurStyle blurStyle

Offset

+double dx

+double dy

5.2 BoxShadow 属性详解

属性 类型 作用 常用值
color Color 阴影颜色 Colors.black.withOpacity(0.2)
blurRadius double 模糊半径 8, 16, 24
offset Offset 阴影偏移 Offset(0, 4)
spreadRadius double 扩散半径 0, 2, 4
blurStyle BlurStyle 模糊样式 BlurStyle.normal

5.3 不同阴影效果

5.3.1 轻微阴影
BoxShadow(
  color: Colors.black.withOpacity(0.1),
  blurRadius: 8,
  offset: const Offset(0, 2),
)
5.3.2 深度阴影
BoxShadow(
  color: Colors.black.withOpacity(0.3),
  blurRadius: 20,
  offset: const Offset(0, 10),
)
5.3.3 彩色阴影
BoxShadow(
  color: Colors.blue.withOpacity(0.5),
  blurRadius: 15,
  offset: const Offset(0, 8),
)
5.3.4 发光效果
BoxShadow(
  color: Colors.amber.withOpacity(0.6),
  blurRadius: 30,
  offset: const Offset(0, 0),
  spreadRadius: 5,
)

5.4 多层阴影叠加

Container(
  decoration: BoxDecoration(
    boxShadow: [
      // 第一层:轻微阴影
      BoxShadow(
        color: Colors.black.withOpacity(0.1),
        blurRadius: 8,
        offset: const Offset(0, 2),
      ),
      // 第二层:彩色阴影
      BoxShadow(
        color: Colors.blue.withOpacity(0.3),
        blurRadius: 16,
        offset: const Offset(0, 8),
      ),
    ],
  ),
  child: Image.asset('assets/image.jpg'),
)

六、 组合效果实战

6.1 圆角 + 阴影

Container(
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(16),
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.2),
        blurRadius: 12,
        offset: const Offset(0, 4),
      ),
    ],
  ),
  child: ClipRRect(
    borderRadius: BorderRadius.circular(16),
    child: Image.asset('assets/image.jpg'),
  ),
)

6.2 圆形 + 阴影(头像)

Container(
  decoration: BoxDecoration(
    shape: BoxShape.circle,
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.2),
        blurRadius: 8,
        offset: const Offset(0, 4),
      ),
    ],
  ),
  child: ClipOval(
    child: Image.asset('assets/avatar.jpg'),
  ),
)

6.3 实际应用:产品卡片

Container(
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(16),
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.15),
        blurRadius: 16,
        offset: const Offset(0, 8),
      ),
    ],
  ),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      ClipRRect(
        borderRadius: const BorderRadius.vertical(
          top: Radius.circular(16),
        ),
        child: Image.network(
          product.imageUrl,
          width: double.infinity,
          height: 200,
          fit: BoxFit.cover,
        ),
      ),
      Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              product.name,
              style: const TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 8),
            Text(
              product.description,
              style: TextStyle(
                fontSize: 14,
                color: Colors.grey[600],
              ),
            ),
            const SizedBox(height: 16),
            Text(
              ${product.price}',
              style: TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
                color: Colors.red[600],
              ),
            ),
          ],
        ),
      ),
    ],
  ),
)

七、 最佳实践

7.1 性能优化

优化点 说明 实现
避免过度裁剪 裁剪操作消耗性能 合理选择裁剪方式
使用 RepaintBoundary 隔离重绘区域 在复杂组件外包裹
缓存图片 减少重复加载 使用 cacheWidthcacheHeight
减少阴影层数 多层阴影影响性能 控制在2-3层以内
// 使用 RepaintBoundary 优化
RepaintBoundary(
  child: Container(
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(16),
      boxShadow: [
        BoxShadow(
          color: Colors.black.withOpacity(0.2),
          blurRadius: 12,
          offset: const Offset(0, 4),
        ),
      ],
    ),
    child: ClipRRect(
      borderRadius: BorderRadius.circular(16),
      child: Image.asset('assets/image.jpg'),
    ),
  ),
)

7.2 鸿蒙平台适配

// 鸿蒙平台特殊处理
import 'dart:io';

Widget build(BuildContext context) {
  final isHarmonyOS = Platform.isAndroid;

  return Container(
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(16),
      // 鸿蒙平台可能需要调整阴影
      boxShadow: isHarmonyOS
          ? [
              BoxShadow(
                color: Colors.black.withOpacity(0.15),
                blurRadius: 10,
                offset: const Offset(0, 3),
              ),
            ]
          : [
              BoxShadow(
                color: Colors.black.withOpacity(0.2),
                blurRadius: 12,
                offset: const Offset(0, 4),
              ),
            ],
    ),
    child: ClipRRect(
      borderRadius: BorderRadius.circular(16),
      child: Image.asset('assets/image.jpg'),
    ),
  );
}

7.3 常见问题解决

问题 原因 解决方案
阴影显示不完整 裁剪范围不足 增加容器 padding
圆角有锯齿 抗锯齿不足 增加图片分辨率
性能下降 过多阴影和裁剪 使用 RepaintBoundary
阴影颜色不对 透明度设置错误 调整 color.withOpacity()

八、 总结

Image Widget 的图片处理技术是 Flutter UI 开发中的重要技能。掌握这些技术,你将能够:

  1. 圆角效果:使用 ClipRRect 灵活控制圆角
  2. 裁剪技术:使用 ClipOval、ClipPath 实现各种形状
  3. 阴影效果:使用 BoxShadow 创建立体感
  4. 组合效果:将多种技术组合使用,创建精美 UI
  5. 性能优化:合理使用 RepaintBoundary 等技术

记住,好的 UI 设计不仅仅是显示图片,而是通过适当的视觉效果提升用户体验。当你能够熟练运用这些图片处理技术时,你就已经掌握了 Flutter UI 开发的重要一环。


欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐