79185251

Date: 2024-11-13 14:10:32
Score: 1.5
Natty:
Report link

Here's how to implement page navigation with your Rive-powered bottom navigation bar.

1. First, Create Your Pages

Create separate page widgets for each tab:

// 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. Modify TabItem Class

Add a title and page widget to your TabItem class:

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. Create a Main Navigation Screen

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. Update CustomTabBar to Show Titles

Modify your CustomTabBar widget to include text labels:

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. Usage

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(),
    );
  }
}

Key Features

  1. Smooth Page Transitions

    • Uses PageView for smooth transitions between pages
    • Animated page changes
    • Disabled swipe navigation for better control
  2. State Management

    • Maintains selected tab state
    • Synchronizes page and tab selections
    • Proper disposal of controllers
  3. UI Customization

    • Animated tab indicators
    • Custom text styling for selected/unselected states
    • Smooth opacity transitions

Additional Tips

  1. Page State Persistence If you want to maintain page states when switching tabs:
PageView(
  controller: _pageController,
  physics: const NeverScrollableScrollPhysics(),
  children: TabItem.tabItemsList.map((tab) {
    return KeyedSubtree(
      key: ValueKey(tab.title),
      child: tab.page,
    );
  }).toList(),
)
  1. Adding Bottom Padding for iPhone
SafeArea(
  bottom: true,
  child: CustomTabBar(...),
)
  1. Handling Deep Links
void navigateToTab(String tabName) {
  final index = TabItem.tabItemsList.indexWhere((tab) => tab.title == tabName);
  if (index != -1) {
    _onTabChange(index);
  }
}

Common Issues and Solutions

  1. Page Not Updating

    • Ensure PageController is properly initialized and disposed
    • Verify onTabChange is being called
    • Check page widgets are properly rebuilt
  2. Animation Issues

    • Make sure Rive assets are properly loaded
    • Verify state machine names match exactly
    • Check animation controllers are properly initialized
  3. Memory Leaks

    • Dispose of controllers in dispose method
    • Clean up any subscriptions or listeners
    • Use StatefulWidgets appropriately

Remember to:

Would you like me to explain any part in more detail or show how to implement specific features?

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Ends in question mark (2):
  • Low reputation (1):
Posted by: Darshan Parmar