Flutter炫酷卡片效果插件radiant_card的使用

Flutter炫酷卡片效果插件radiant_card的使用

radiant_card

本应用展示了Flutter自定义着色器功能。通过将Phong着色程序应用于GlossyCard小部件来模仿行为。

注意:着色器仅在Android/iOS以及可能的桌面操作系统上有效。

完整示例Demo

以下是一个完整的示例代码,展示了如何使用radiant_card插件来创建炫酷的卡片效果。

import 'package:flutter/material.dart';
import 'package:marquee/marquee.dart';
import 'package:radiant_card/radiant_card.dart';
import 'package:vector_math/vector_math.dart' hide Colors;

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const _widthInCards = 1;
  static const _heightInCards = 1;

  static const _viewportWidth = 1;

  static const _gridViewPadding = 16.0;
  static const _cardSpacing = 8.0;
  static const _cardAspectRatio = 711.0 / 990.0;

  final _controller = ScrollController();

  double _scrollPosition = 0.0;

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      _scrollPosition = _controller.offset;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('卡片演示'),
      ),
      body: ImageContextWidget(
        imageFiles: const ["assets/images/pikachu.png", "assets/images/mask_1.png"], // todo 可能移除第一张图片
        builder: (context, images) {
          return LayoutBuilder(builder: (context, constraints) {
            final containerWidth = constraints.maxWidth;
            final containerHeight = constraints.maxHeight;
            final cardWidth = (containerWidth - 2 * _gridViewPadding - (_widthInCards - 1) * _cardSpacing) / _widthInCards;
            final cardHeight = cardWidth / _cardAspectRatio;
            return GridView.count(
              padding: const EdgeInsets.all(_gridViewPadding),
              crossAxisCount: _widthInCards,
              crossAxisSpacing: _cardSpacing,
              mainAxisSpacing: _cardSpacing,
              childAspectRatio: _cardAspectRatio,
              controller: _controller,
              children: List.generate(
                _widthInCards * _heightInCards,
                (index) {
                  final i = index % _widthInCards;
                  final j = index ~/ _widthInCards;

                  final cardCenterContainerXCoord = _gridViewPadding + i * (cardWidth + _cardSpacing) + cardWidth / 2 - containerWidth / 2;
                  final cardCenterContainerYCoord = _gridViewPadding + j * (cardHeight + _cardSpacing) + cardHeight / 2 - containerHeight / 2;

                  final cardCenterWorldXCoord = cardCenterContainerXCoord / containerWidth * _viewportWidth;
                  final cardCenterWorldYCoord = (cardCenterContainerYCoord - _scrollPosition) * _viewportWidth / containerWidth;

                  return WidgetToImageBuilder(
                    child: const AttaboyWidget(),
                    builder: (context, widgetImage) => widgetImage == null
                        ? const SizedBox.shrink()
                        : RotatableShadedCard(
                            mainTexture: widgetImage,
                            mask: images[1],
                            centerPos: Vector3(cardCenterWorldXCoord, cardCenterWorldYCoord, 0),
                            worldSize: Size(cardWidth / containerWidth * _viewportWidth, cardHeight / containerWidth * _viewportWidth),
                          ),
                  );
                },
              ),
            );
          });
        },
        emptyBuilder: (context) => const SizedBox.shrink(),
      ),
      backgroundColor: Colors.white,
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.refresh),
        onPressed: () {
          setState(() {});
        },
      ),
    );
  }
}

class AttaboyWidget extends StatelessWidget {
  const AttaboyWidget({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue,
      alignment: Alignment.center,
      child: Stack(
        fit: StackFit.expand,
        children: [
          Image.asset(
            'assets/images/den.jpg', // TODO 确保此图像存在
            fit: BoxFit.cover,
          ),
          Positioned(
            bottom: 80,
            left: 0,
            right: 0,
            child: Container(
              color: Colors.black,
              height: 48,
              child: Marquee(
                text: "完美地保持住了",
                crossAxisAlignment: CrossAxisAlignment.center,
                style: const TextStyle(fontSize: 24, color: Colors.white, fontWeight: FontWeight.bold),
                blankSpace: 32.0,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

说明

  1. 导入必要的库

    import 'package:flutter/material.dart';
    import 'package:marquee/marquee.dart';
    import 'package:radiant_card/radiant_card.dart';
    import 'package:vector_math/vector_math.dart' hide Colors;
    
  2. 初始化应用

    void main() {
      runApp(const MyApp());
    }
    
  3. 主应用类

    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData(),
          home: const MyHomePage(),
        );
      }
    }
    
  4. 主页状态类

    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key});
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      static const _widthInCards = 1;
      static const _heightInCards = 1;
    
      static const _viewportWidth = 1;
    
      static const _gridViewPadding = 16.0;
      static const _cardSpacing = 8.0;
      static const _cardAspectRatio = 711.0 / 990.0;
    
      final _controller = ScrollController();
    
      double _scrollPosition = 0.0;
    
      @override
      void initState() {
        super.initState();
        _controller.addListener(() {
          _scrollPosition = _controller.offset;
          setState(() {});
        });
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('卡片演示'),
          ),
          body: ImageContextWidget(
            imageFiles: const ["assets/images/pikachu.png", "assets/images/mask_1.png"],
            builder: (context, images) {
              return LayoutBuilder(builder: (context, constraints) {
                final containerWidth = constraints.maxWidth;
                final containerHeight = constraints.maxHeight;
                final cardWidth = (containerWidth - 2 * _gridViewPadding - (_widthInCards - 1) * _cardSpacing) / _widthInCards;
                final cardHeight = cardWidth / _cardAspectRatio;
                return GridView.count(
                  padding: const EdgeInsets.all(_gridViewPadding),
                  crossAxisCount: _widthInCards,
                  crossAxisSpacing: _cardSpacing,
                  mainAxisSpacing: _cardSpacing,
                  childAspectRatio: _cardAspectRatio,
                  controller: _controller,
                  children: List.generate(
                    _widthInCards * _heightInCards,
                    (index) {
                      final i = index % _widthInCards;
                      final j = index ~/ _widthInCards;
    
                      final cardCenterContainerXCoord = _gridViewPadding + i * (cardWidth + _cardSpacing) + cardWidth / 2 - containerWidth / 2;
                      final cardCenterContainerYCoord = _gridViewPadding + j * (cardHeight + _cardSpacing) + cardHeight / 2 - containerHeight / 2;
    
                      final cardCenterWorldXCoord = cardCenterContainerXCoord / containerWidth * _viewportWidth;
                      final cardCenterWorldYCoord = (cardCenterContainerYCoord - _scrollPosition) * _viewportWidth / containerWidth;
    
                      return WidgetToImageBuilder(
                        child: const AttaboyWidget(),
                        builder: (context, widgetImage) => widgetImage == null
                            ? const SizedBox.shrink()
                            : RotatableShadedCard(
                                mainTexture: widgetImage,
                                mask: images[1],
                                centerPos: Vector3(cardCenterWorldXCoord, cardCenterWorldYCoord, 0),
                                worldSize: Size(cardWidth / containerWidth * _viewportWidth, cardHeight / containerWidth * _viewportWidth),
                              ),
                      );
                    },
                  ),
                );
              });
            },
            emptyBuilder: (context) => const SizedBox.shrink(),
          ),
          backgroundColor: Colors.white,
          floatingActionButton: FloatingActionButton(
            child: const Icon(Icons.refresh),
            onPressed: () {
              setState(() {});
            },
          ),
        );
      }
    }
    
  5. 卡片内容组件

    class AttaboyWidget extends StatelessWidget {
      const AttaboyWidget({
        super.key,
      });
    
      @override
      Widget build(BuildContext context) {
        return Container(
          color: Colors.blue,
          alignment: Alignment.center,
          child: Stack(
            fit: StackFit.expand,
            children: [
              Image.asset(
                'assets/images/den.jpg',
                fit: BoxFit.cover,
              ),
              Positioned(
                bottom: 80,
                left: 0,
                right: 0,
                child: Container(
                  color: Colors.black,
                  height: 48,
                  child: Marquee(
                    text: "完美地保持住了",
                    crossAxisAlignment: CrossAxisAlignment.center,
                    style: const TextStyle(fontSize: 24, color: Colors.white, fontWeight: FontWeight.bold),
                    blankSpace: 32.0,
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }
    

更多关于Flutter炫酷卡片效果插件radiant_card的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter炫酷卡片效果插件radiant_card的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


radiant_card 是一个用于 Flutter 的插件,可以帮助开发者轻松实现炫酷的卡片效果。这个插件提供了多种卡片样式和动画效果,可以让你的应用界面更加吸引人。以下是如何使用 radiant_card 插件的基本步骤。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 radiant_card 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  radiant_card: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在你的 Dart 文件中导入 radiant_card 插件:

import 'package:radiant_card/radiant_card.dart';

3. 使用 RadiantCard

RadiantCardradiant_card 插件提供的核心组件。你可以通过设置不同的属性来自定义卡片的外观和行为。

基本用法

RadiantCard(
  width: 200,
  height: 200,
  color: Colors.blue,
  borderRadius: BorderRadius.circular(10),
  child: Center(
    child: Text(
      'Hello, RadiantCard!',
      style: TextStyle(color: Colors.white, fontSize: 20),
    ),
  ),
)

添加阴影效果

RadiantCard(
  width: 200,
  height: 200,
  color: Colors.blue,
  borderRadius: BorderRadius.circular(10),
  elevation: 10,  // 阴影效果
  child: Center(
    child: Text(
      'Hello, RadiantCard!',
      style: TextStyle(color: Colors.white, fontSize: 20),
    ),
  ),
)

添加渐变背景

RadiantCard(
  width: 200,
  height: 200,
  gradient: LinearGradient(
    colors: [Colors.blue, Colors.purple],
    begin: Alignment.topLeft,
    end: Alignment.bottomRight,
  ),
  borderRadius: BorderRadius.circular(10),
  child: Center(
    child: Text(
      'Hello, RadiantCard!',
      style: TextStyle(color: Colors.white, fontSize: 20),
    ),
  ),
)

添加动画效果

RadiantCard(
  width: 200,
  height: 200,
  color: Colors.blue,
  borderRadius: BorderRadius.circular(10),
  animationDuration: Duration(seconds: 1),  // 动画持续时间
  animationCurve: Curves.easeInOut,  // 动画曲线
  child: Center(
    child: Text(
      'Hello, RadiantCard!',
      style: TextStyle(color: Colors.white, fontSize: 20),
    ),
  ),
)

4. 自定义更多属性

RadiantCard 提供了多种属性供你自定义,包括但不限于:

  • widthheight:卡片的宽度和高度。
  • color:卡片的背景颜色。
  • gradient:卡片的渐变背景。
  • borderRadius:卡片的圆角半径。
  • elevation:卡片的阴影效果。
  • animationDuration:动画持续时间。
  • animationCurve:动画曲线。
  • child:卡片的内容。

5. 完整示例

以下是一个完整的示例,展示了如何使用 RadiantCard 创建一个带有渐变背景和动画效果的卡片:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('RadiantCard Example'),
        ),
        body: Center(
          child: RadiantCard(
            width: 200,
            height: 200,
            gradient: LinearGradient(
              colors: [Colors.blue, Colors.purple],
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
            ),
            borderRadius: BorderRadius.circular(10),
            animationDuration: Duration(seconds: 1),
            animationCurve: Curves.easeInOut,
            child: Center(
              child: Text(
                'Hello, RadiantCard!',
                style: TextStyle(color: Colors.white, fontSize: 20),
              ),
            ),
          ),
        ),
      ),
    );
  }
}
回到顶部