Flutter底部导航栏插件rolling_nav_bar的使用

Flutter底部导航栏插件rolling_nav_bar的使用

简介

rolling_nav_bar 是一个灵感来源于设计作品集(Dribbble)的设计,并且具有高度可定制动画、颜色和形状的底部导航栏插件。它支持3、4或5个导航元素,并允许用户自定义每个导航项。

开始使用

基本用法

首先,将 RollingNavBar 小部件放置在 ScaffoldbottomNavigationBar 槽中,并将其包裹在一个提供最大高度的容器中。例如:

Scaffold(
  bottomNavigationBar: Container(
    height: 95,
    child: RollingNavBar(
      // 导航项配置
    ),
  )
);

或者,也可以直接通过 Stack 小部件放置:

Scaffold(
  body: Stack(
    children: <Widget>[
      Positioned(
        bottom: 0,
        height: 95,
        width: MediaQuery.of(context).size.width,
        child: RollingNavBar(
          // 导航项配置
        ),
      )
    ]
  )
);

自定义

使用图标数据

RollingNavBar 支持通过 iconData 构造函数指定导航项图标和颜色。以下是一个简单的例子:

RollingNavBar.iconData(
  iconData: [
    Icons.home,
    Icons.people,
    Icons.settings,
  ],
  indicatorColors: [
    Colors.red,
    Colors.yellow,
    Colors.blue,
  ],
)

使用小部件构建器

另一种方式是通过 builder 构造函数指定导航项的小部件。这种方式虽然提供了更大的灵活性,但需要手动管理激活状态。

RollingNavBar.builder(
  builder: (context, index, info, update) {
    var textStyle = index == info.nextIndex
      ? TextStyle(color: Colors.white)
      : TextStyle(color: Colors.grey);
    return Text('${index + 1}', style: textStyle);
  },
  indicatorColors: [
    Colors.red,
    Colors.yellow,
    Colors.blue,
  ],
  numChildren: 3,
)

动画类型

Roll 动画

这是默认的滚动动画效果:

RollingNavBar.iconData(
  animationCurve: Curves.easeOut,
  animationType: AnimationType.roll,
  baseAnimationSpeed: 200,
  iconData: [
    Icons.home,
    Icons.people,
    Icons.settings,
  ],
)

Roll Example

Shrink Out In 动画

这种动画效果是缩放和淡出的效果:

RollingNavBar.iconData(
  animationCurve: Curves.linear,
  animationType: AnimationType.shrinkOutIn,
  baseAnimationSpeed: 500,
  iconData: [
    Icons.home,
    Icons.people,
    Icons.settings,
  ],
)

Shrink-out-in Example

Spin Out In 动画

这种动画效果是旋转并淡出的效果:

RollingNavBar.iconData(
  animationCurve: Curves.linear,
  animationType: AnimationType.spinOutIn,
  baseAnimationSpeed: 500,
  iconData: [
    Icons.home,
    Icons.people,
    Icons.settings,
  ],
)

Spin-out-in Example

Snap 动画

这是一种无动画的效果:

RollingNavBar.iconData(
  animationType: AnimationType.snap,
  iconData: [
    Icons.home,
    Icons.people,
    Icons.settings,
  ],
)

Snap Example

钩入动画

为了实现与导航栏进度同步的视觉效果,可以使用 onTaponAnimate 回调。例如,当导航栏切换时同步背景颜色的变化。

图标文字

可以通过 iconText 参数为导航项添加文字描述:

RollingNavBar.iconData(
  iconColors: [Colors.grey[800]],
  iconData: [
    Icons.home,
    Icons.people,
    Icons.settings,
  ],
  iconText: [
    Text('Home', style: TextStyle(color: Colors.grey, fontSize: 12)),
    Text('Friends', style: TextStyle(color: Colors.grey, fontSize: 12)),
    Text('Settings', style: TextStyle(color: Colors.grey, fontSize: 12)),
  ]
)

Icon Text Example

图标徽章

通过结合 badges 参数和 badges 库,可以轻松为导航栏添加徽章:

RollingNavBar.iconData(
  badges: [
    Text('1', style: TextStyle(Colors.white)),
    Text('1', style: TextStyle(Colors.white)),
    null,
    null,
    Text('1', style: TextStyle(Colors.white)),
  ],
  iconData: [
    Icons.home,
    Icons.people,
    Icons.account_circle,
    Icons.chat,
    Icons.settings,
  ],
)

Badges Example

程序化更改导航栏

可以通过传递新的 activeIndex 来程序化地更改活动的导航栏选项。确保同步更新状态:

class _MyAppState extends State<MyApp> {
  int activeIndex;

  void _onTap(int index) {
    setState(() {
      activeIndex = index;
    });
  }

  void changeActiveIndex(int index) {
    setState(() {
      activeIndex = index;
    });
  }

  Widget build(BuildContext context) {
    return RollingNavBar.iconData(
      activeIndex: activeIndex,
      iconData: iconData,
      onTap: _onTap,
    );
  }
}

完整示例代码

以下是一个完整的示例代码,展示了如何使用 rolling_nav_bar 插件:

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:polygon_clipper/polygon_clipper.dart';
import 'package:rolling_nav_bar/indexed.dart';
import 'package:rolling_nav_bar/rolling_nav_bar.dart';

void main() => runApp(MyApp());

double scaledHeight(BuildContext context, double baseSize) {
  return baseSize * (MediaQuery.of(context).size.height / 800);
}

double scaledWidth(BuildContext context, double baseSize) {
  return baseSize * (MediaQuery.of(context).size.width / 375);
}

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Color logoColor;
  int activeIndex;

  var iconData = [
    Icons.home,
    Icons.people,
    Icons.account_circle,
    Icons.chat,
    Icons.settings,
  ];

  var badges = [null, null, null, null, null];

  var iconText = [
    Text('Home', style: TextStyle(color: Colors.grey, fontSize: 12)),
    Text('Friends', style: TextStyle(color: Colors.grey, fontSize: 12)),
    Text('Account', style: TextStyle(color: Colors.grey, fontSize: 12)),
    Text('Chat', style: TextStyle(color: Colors.grey, fontSize: 12)),
    Text('Settings', style: TextStyle(color: Colors.grey, fontSize: 12)),
  ];

  var indicatorColors = [
    Colors.red,
    Colors.orange,
    Colors.green,
    Colors.blue,
    Colors.purple,
  ];

  List<Widget> get badgeWidgets => indexed(badges)
      .map((Indexed indexed) =>
          indexed.value != null
              ? Text(indexed.value.toString(),
                  style: TextStyle(
                    color: indexed.index == activeIndex
                        ? indicatorColors[indexed.index]
                        : Colors.white,
                  ))
              : null)
      .toList();

  [@override](/user/override)
  void initState() {
    logoColor = Colors.red[600];
    activeIndex = 0;
    super.initState();
  }

  void incrementIndex() {
    setState(() {
      activeIndex = activeIndex < (iconData.length - 1) ? activeIndex + 1 : 0;
    });
  }

  _onAnimate(AnimationUpdate update) {
    setState(() {
      logoColor = update.color;
    });
  }

  _onTap(int index) {
    if (activeIndex == index) {
      _incrementBadge();
    }
    activeIndex = index;
    setState(() {});
  }

  void _incrementBadge() {
    badges[activeIndex] =
        badges[activeIndex] == null ? 1 : badges[activeIndex] + 1;
    setState(() {});
  }

  List<Widget> get builderChildren => const [
        Text('1', style: TextStyle(color: Colors.grey)),
        Text('2', style: TextStyle(color: Colors.grey)),
        Text('3', style: TextStyle(color: Colors.grey)),
      ];

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        scaffoldBackgroundColor: Colors.blue[100],
      ),
      home: Directionality(
        textDirection: Directionality.of(context) ?? TextDirection.ltr,
        child: Builder(
          builder: (BuildContext context) {
            double largeIconHeight = MediaQuery.of(context).size.width;
            double navBarHeight = scaledHeight(context, 85);
            double topOffset = (MediaQuery.of(context).size.height -
                    largeIconHeight -
                    MediaQuery.of(context).viewInsets.top -
                    (navBarHeight * 2)) /
                2;
            return Scaffold(
              floatingActionButton: FloatingActionButton(
                backgroundColor: logoColor,
                child: Icon(Icons.add),
                onPressed: _incrementBadge,
              ),
              appBar: AppBar(
                title: Text('Rolling Nav Bar: Tab ${activeIndex + 1}'),
              ),
              body: Stack(
                children: <Widget>[
                  Positioned(
                    top: topOffset,
                    height: largeIconHeight,
                    width: largeIconHeight,
                    child: GestureDetector(
                      onTap: incrementIndex,
                      child: ClipPolygon(
                        sides: 6,
                        borderRadius: 15,
                        child: Container(
                          height: largeIconHeight,
                          width: largeIconHeight,
                          color: logoColor,
                          child: Center(
                            child: Padding(
                              padding: EdgeInsets.fromLTRB(0, 100, 30, 0),
                              child: Transform(
                                transform: Matrix4.skew(0.1, -0.50),
                                child: Text(
                                  'Rolling\nNav Bar',
                                  textAlign: TextAlign.center,
                                  style: TextStyle(
                                    color: Colors.white,
                                    fontSize: scaledWidth(context, 63),
                                    fontFeatures: [FontFeature.enable('smcp')],
                                    shadows: [
                                      Shadow(
                                        offset: Offset(5, 5),
                                        blurRadius: 3.0,
                                        color: Color.fromARGB(255, 0, 0, 0),
                                      ),
                                      Shadow(
                                        offset: Offset(5, 5),
                                        blurRadius: 8.0,
                                        color: Color.fromARGB(125, 0, 0, 255),
                                      ),
                                    ],
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
              bottomNavigationBar: Container(
                height: navBarHeight,
                width: MediaQuery.of(context).size.width,
                child: RollingNavBar.iconData(
                  activeBadgeColors: [Colors.white],
                  activeIndex: activeIndex,
                  animationCurve: Curves.linear,
                  animationType: AnimationType.roll,
                  baseAnimationSpeed: 200,
                  badges: badgeWidgets,
                  iconData: iconData,
                  iconColors: [Colors.grey[800]],
                  iconText: iconText,
                  indicatorColors: indicatorColors,
                  iconSize: 25,
                  indicatorRadius: scaledHeight(context, 30),
                  onAnimate: _onAnimate,
                  onTap: _onTap,
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

更多关于Flutter底部导航栏插件rolling_nav_bar的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter底部导航栏插件rolling_nav_bar的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


rolling_nav_bar 是一个 Flutter 插件,用于创建具有滚动效果的底部导航栏。它提供了一种美观的方式来在应用程序的不同部分之间进行导航。以下是使用 rolling_nav_bar 插件的步骤和示例代码。

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 rolling_nav_bar 依赖:

dependencies:
  flutter:
    sdk: flutter
  rolling_nav_bar: ^1.0.0+1  # 请检查最新版本

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

2. 导入包

在需要使用 rolling_nav_bar 的 Dart 文件中导入包:

import 'package:rolling_nav_bar/rolling_nav_bar.dart';

3. 使用 RollingNavBar

下面是一个简单的示例,展示如何在 Flutter 应用中使用 rolling_nav_bar

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Rolling Nav Bar Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: RollingNavBarExample(),
    );
  }
}

class RollingNavBarExample extends StatefulWidget {
  [@override](/user/override)
  _RollingNavBarExampleState createState() => _RollingNavBarExampleState();
}

class _RollingNavBarExampleState extends State<RollingNavBarExample> {
  int _selectedIndex = 0;

  final List<Widget> _pages = [
    Center(child: Text('Home Page')),
    Center(child: Text('Search Page')),
    Center(child: Text('Profile Page')),
  ];

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Rolling Nav Bar Example'),
      ),
      body: _pages[_selectedIndex],
      bottomNavigationBar: RollingNavBar(
        color: Colors.grey,
        activeColor: Colors.blue,
        iconSize: 24.0,
        animationCurve: Curves.easeInOut,
        animationDuration: Duration(milliseconds: 300),
        items: [
          RollingNavBarItem(Icons.home, 'Home'),
          RollingNavBarItem(Icons.search, 'Search'),
          RollingNavBarItem(Icons.person, 'Profile'),
        ],
        onTap: (index) {
          setState(() {
            _selectedIndex = index;
          });
        },
      ),
    );
  }
}
回到顶部