Flutter Rive 代码 如何在子菜单图标下添加文本?

问题描述 投票:0回答:1

Flutter Rive 代码 如何在子菜单图标下添加文本?

如何在菜单图标下添加文本,如图所示?

我不知道,因为我之前没有尝试过 flutter rive,请帮忙。

我在子菜单图标下添加了文本,我解决了这个问题。 如何在页面之间切换?如何创建一个新页面并在点击时切换到该页面?

在此输入图片描述

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

class TabItem {
  TabItem({
    this.stateMachine = "",
    this.artboard = "",
    this.status,
  });

  UniqueKey? id = UniqueKey();
  String stateMachine;
  String artboard;
  late SMIBool? status;

  static List<TabItem> tabItemsList = [
    TabItem(
      stateMachine: "CHAT_Interactivity",
      artboard: "CHAT",
    ),
    TabItem(
      stateMachine: "SEARCH_Interactivity",
      artboard: "SEARCH",
    ),
    TabItem(stateMachine: "TIMER_Interactivity", artboard: "TIMER"),
    TabItem(stateMachine: "BELL_Interactivity", artboard: "BELL"),
    TabItem(stateMachine: "USER_Interactivity", artboard: "USER"),
  ];
}

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:rive/rive.dart' hide LinearGradient;
import 'package:flutter_samples/samples/ui/rive_app/models/tab_item.dart';
import 'package:flutter_samples/samples/ui/rive_app/theme.dart';
import 'package:flutter_samples/samples/ui/rive_app/assets.dart' as app_assets;

class CustomTabBar extends StatefulWidget {
  const CustomTabBar({Key? key, required this.onTabChange}) : super(key: key);

  final Function(int tabIndex) onTabChange;

  @override
  State<CustomTabBar> createState() => _CustomTabBarState();
}

class _CustomTabBarState extends State<CustomTabBar> {
  final List<TabItem> _icons = TabItem.tabItemsList;

  int _selectedTab = 0;

  void _onRiveIconInit(Artboard artboard, index) {
    final controller = StateMachineController.fromArtboard(
        artboard, _icons[index].stateMachine);
    artboard.addController(controller!);

    _icons[index].status = controller.findInput<bool>("active") as SMIBool;
  }

  void onTabPress(int index) {
    if (_selectedTab != index) {
      setState(() {
        _selectedTab = index;
      });
      widget.onTabChange(index);

      _icons[index].status!.change(true);
      Future.delayed(const Duration(seconds: 1), () {
        _icons[index].status!.change(false);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Container(
        margin: const EdgeInsets.fromLTRB(24, 0, 24, 8),
        padding: const EdgeInsets.all(1),
        constraints: const BoxConstraints(maxWidth: 768),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(24),
          gradient: LinearGradient(colors: [
            Colors.white.withOpacity(0.5),
            Colors.white.withOpacity(0)
          ]),
        ),
        child: Container(
          // Clip to avoid the tab touch outside the border radius area
          clipBehavior: Clip.hardEdge,
          decoration: BoxDecoration(
            color: RiveAppTheme.background2.withOpacity(0.8),
            borderRadius: BorderRadius.circular(24),
            boxShadow: [
              BoxShadow(
                color: RiveAppTheme.background2.withOpacity(0.3),
                blurRadius: 20,
                offset: const Offset(0, 20),
              )
            ],
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: List.generate(_icons.length, (index) {
              TabItem icon = _icons[index];

              return Expanded(
                key: icon.id,
                child: CupertinoButton(
                  padding: const EdgeInsets.all(12),
                  child: AnimatedOpacity(
                    opacity: _selectedTab == index ? 1 : 0.5,
                    duration: const Duration(milliseconds: 200),
                    child: Stack(
                        clipBehavior: Clip.none,
                        alignment: Alignment.center,
                        children: [
                          Positioned(
                            top: -4,
                            child: AnimatedContainer(
                              duration: const Duration(milliseconds: 200),
                              height: 4,
                              width: _selectedTab == index ? 20 : 0,
                              decoration: BoxDecoration(
                                color: RiveAppTheme.accentColor,
                                borderRadius: BorderRadius.circular(2),
                              ),
                            ),
                          ),
                          SizedBox(
                            height: 36,
                            width: 36,
                            child: RiveAnimation.asset(
                              app_assets.iconsRiv,
                              stateMachines: [icon.stateMachine],
                              artboard: icon.artboard,
                              onInit: (artboard) {
                                _onRiveIconInit(artboard, index);
                              },
                            ),
                          )
                        ]),
                  ),
                  onPressed: () {
                    onTabPress(index);
                  },
                ),
              );
            }),
          ),
        ),
      ),
    );
  }
}

Flutter Rive 代码 如何在子菜单图标下添加文本?

如何在菜单图标下添加文本,如图所示?

我不知道,因为我之前没有尝试过 flutter rive,请帮忙。

我在子菜单图标下添加了文本,我解决了这个问题。 如何在页面之间切换?如何创建一个新页面并在点击时切换到该页面?

flutter flutter-dependencies flutter-animation
1个回答
0
投票

以下是如何使用 Rive 驱动的底部导航栏实现页面导航。

1.首先,创建您的页面

为每个选项卡创建单独的页面小部件:

// chat_page.dart
class ChatPage extends StatelessWidget {
  const ChatPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Chat Page'),
    );
  }
}

// Similar for other pages
class SearchPage extends StatelessWidget {...}
class TimerPage extends StatelessWidget {...}
class NotificationsPage extends StatelessWidget {...}
class ProfilePage extends StatelessWidget {...}

2.修改TabItem类

将标题和页面小部件添加到 TabItem 类中:

class TabItem {
  TabItem({
    required this.stateMachine,
    required this.artboard,
    required this.title,
    required this.page,
    this.status,
  });

  UniqueKey? id = UniqueKey();
  String stateMachine;
  String artboard;
  String title;
  Widget page;
  late SMIBool? status;

  static List<TabItem> tabItemsList = [
    TabItem(
      stateMachine: "CHAT_Interactivity",
      artboard: "CHAT",
      title: "Chat",
      page: const ChatPage(),
    ),
    TabItem(
      stateMachine: "SEARCH_Interactivity",
      artboard: "SEARCH",
      title: "Search",
      page: const SearchPage(),
    ),
    TabItem(
      stateMachine: "TIMER_Interactivity", 
      artboard: "TIMER",
      title: "Timer",
      page: const TimerPage(),
    ),
    TabItem(
      stateMachine: "BELL_Interactivity", 
      artboard: "BELL",
      title: "Notifications",
      page: const NotificationsPage(),
    ),
    TabItem(
      stateMachine: "USER_Interactivity", 
      artboard: "USER",
      title: "Profile",
      page: const ProfilePage(),
    ),
  ];
}

3.创建主导航屏幕

class MainScreen extends StatefulWidget {
  const MainScreen({Key? key}) : super(key: key);

  @override
  State<MainScreen> createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  final PageController _pageController = PageController();
  int _selectedIndex = 0;

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

  void _onTabChange(int index) {
    setState(() {
      _selectedIndex = index;
    });
    _pageController.animateToPage(
      index,
      duration: const Duration(milliseconds: 300),
      curve: Curves.easeInOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // Pages
          PageView(
            controller: _pageController,
            physics: const NeverScrollableScrollPhysics(), // Disable swipe
            children: TabItem.tabItemsList.map((tab) => tab.page).toList(),
          ),
          // Custom Tab Bar
          Positioned(
            bottom: 0,
            left: 0,
            right: 0,
            child: CustomTabBar(
              onTabChange: _onTabChange,
            ),
          ),
        ],
      ),
    );
  }
}

4.更新 CustomTabBar 以显示标题

修改您的 CustomTabBar 小部件以包含文本标签:

class _CustomTabBarState extends State<CustomTabBar> {
  // ... existing code ...

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Container(
        margin: const EdgeInsets.fromLTRB(24, 0, 24, 8),
        padding: const EdgeInsets.all(1),
        constraints: const BoxConstraints(maxWidth: 768),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(24),
          gradient: LinearGradient(colors: [
            Colors.white.withOpacity(0.5),
            Colors.white.withOpacity(0)
          ]),
        ),
        child: Container(
          clipBehavior: Clip.hardEdge,
          decoration: BoxDecoration(
            color: RiveAppTheme.background2.withOpacity(0.8),
            borderRadius: BorderRadius.circular(24),
            boxShadow: [
              BoxShadow(
                color: RiveAppTheme.background2.withOpacity(0.3),
                blurRadius: 20,
                offset: const Offset(0, 20),
              )
            ],
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: List.generate(_icons.length, (index) {
              TabItem icon = _icons[index];

              return Expanded(
                key: icon.id,
                child: CupertinoButton(
                  padding: const EdgeInsets.all(12),
                  child: AnimatedOpacity(
                    opacity: _selectedTab == index ? 1 : 0.5,
                    duration: const Duration(milliseconds: 200),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Stack(
                          clipBehavior: Clip.none,
                          alignment: Alignment.center,
                          children: [
                            Positioned(
                              top: -4,
                              child: AnimatedContainer(
                                duration: const Duration(milliseconds: 200),
                                height: 4,
                                width: _selectedTab == index ? 20 : 0,
                                decoration: BoxDecoration(
                                  color: RiveAppTheme.accentColor,
                                  borderRadius: BorderRadius.circular(2),
                                ),
                              ),
                            ),
                            SizedBox(
                              height: 36,
                              width: 36,
                              child: RiveAnimation.asset(
                                app_assets.iconsRiv,
                                stateMachines: [icon.stateMachine],
                                artboard: icon.artboard,
                                onInit: (artboard) {
                                  _onRiveIconInit(artboard, index);
                                },
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 4),
                        Text(
                          icon.title,
                          style: TextStyle(
                            fontSize: 12,
                            color: _selectedTab == index
                                ? RiveAppTheme.accentColor
                                : Colors.grey,
                          ),
                        ),
                      ],
                    ),
                  ),
                  onPressed: () {
                    onTabPress(index);
                  },
                ),
              );
            }),
          ),
        ),
      ),
    );
  }
}

5.使用方法

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Rive Navigation Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MainScreen(),
    );
  }
}

主要特点

  1. 平滑页面转换

    • 使用 PageView 实现页面之间的平滑过渡
    • 动画页面变化
    • 禁用滑动导航以获得更好的控制
  2. 状态管理

    • 保持选定的选项卡状态
    • 同步页面和选项卡选择
    • 正确处置控制器
  3. UI定制

    • 动画选项卡指示器
    • 选定/未选定状态的自定义文本样式
    • 平滑的不透明度过渡

额外提示

  1. 页面状态持久化 如果您想在切换选项卡时保持页面状态:
PageView(
  controller: _pageController,
  physics: const NeverScrollableScrollPhysics(),
  children: TabItem.tabItemsList.map((tab) {
    return KeyedSubtree(
      key: ValueKey(tab.title),
      child: tab.page,
    );
  }).toList(),
)
  1. 为 iPhone 添加底部填充
SafeArea(
  bottom: true,
  child: CustomTabBar(...),
)
  1. 处理深层链接
void navigateToTab(String tabName) {
  final index = TabItem.tabItemsList.indexWhere((tab) => tab.title == tabName);
  if (index != -1) {
    _onTabChange(index);
  }
}

常见问题及解决方案

  1. 页面未更新

    • 确保 PageController 已正确初始化和处置
    • 验证 onTabChange 是否被调用
    • 检查页面小部件是否正确重建
  2. 动画问题

    • 确保 Rive 资源已正确加载
    • 验证状态机名称完全匹配
    • 检查动画控制器是否正确初始化
  3. 内存泄漏

    • 在dispose方法中处置控制器
    • 清理所有订阅或侦听器
    • 适当使用 StatefulWidgets

记住:

  • 正确处理页面状态
  • 添加适当的错误处理
  • 考虑特定于平台的行为
  • 彻底测试导航

您希望我更详细地解释任何部分或展示如何实现特定功能吗?

© www.soinside.com 2019 - 2024. All rights reserved.