如何创建这种带有弯曲背景的底部导航栏?

时间:2021-02-10 10:58:18

标签: flutter flutter-layout bottomnavigationview flutter-bottomnavigation

enter image description here

我尝试了一些包和图像堆栈,但它对我不起作用。任何帮助都会很棒。

带有曲线的背景图像不起作用。

1 个答案:

答案 0 :(得分:1)

这对你有用吗?

enter image description here

您应该可以使用自定义 NotchedShape 来做到这一点:

class CustomNotchedShape extends NotchedShape {
  const CustomNotchedShape();

  @override
  Path getOuterPath(Rect host, Rect guest) {
    final x = math.min(host.width / 20, host.height / 3);
    final guestMargin = guest == null ? 0 : 1.0;
    if (guest == null || !host.overlaps(guest)) {
      guest = Rect.fromCenter(
          center: Offset(host.width / 2, host.top), width: 0, height: 0);
    }

    num degToRad(num deg) => deg * (math.pi / 180.0);

    return Path()
      ..moveTo(host.left, host.bottom)
      ..quadraticBezierTo(
          host.left + x, host.bottom, host.left + x, host.bottom - x)
      ..lineTo(host.left + x, host.top + x)
      ..quadraticBezierTo(host.left + x, host.top, host.left + 2 * x, host.top)
      ..lineTo(guest.left, host.top)
      ..arcTo(
          Rect.fromCenter(
              center: guest.center,
              width: guest.width + 2 * guestMargin,
              height: guest.width + 2 * guestMargin),
          degToRad(180),
          degToRad(-180),
          false)
      ..lineTo(host.right - 2 * x, host.top)
      ..quadraticBezierTo(
          host.right - x, host.top, host.right - x, host.top + x)
      ..lineTo(host.right - x, host.bottom - x)
      ..quadraticBezierTo(host.right - x, host.bottom, host.right, host.bottom)
      ..close();
  }
}

然后,您使用此 CustomNotchedShape 作为您的 shapeBottomAppBar

class MyBottomNavigationBar extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final _currentIndex = useState(1);
    void navigateTo(int index) => _currentIndex.value = index;
    bool active(int index) => _currentIndex.value == index;

    return BottomAppBar(
      color: Theme.of(context).primaryColor,
      shape: CustomNotchedShape(),
      child: Container(
        height: 50,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0),
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 12.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                IconButton(
                  onPressed: () => navigateTo(0),
                  icon: Icon(Icons.home_outlined),
                  color: active(0) ? Colors.white : Colors.black,
                ),
                IconButton(
                  onPressed: () => navigateTo(1),
                  icon: Icon(Icons.horizontal_split_outlined),
                  color: active(1) ? Colors.white : Colors.black,
                ),
                Padding(
                  padding: EdgeInsets.only(top: 24.0),
                  child: Text('New task'),
                ),
                IconButton(
                  onPressed: () => navigateTo(2),
                  icon: Icon(Icons.access_time_outlined),
                  color: active(2) ? Colors.white : Colors.black,
                ),
                IconButton(
                  onPressed: () => navigateTo(3),
                  icon: Icon(Icons.settings_outlined),
                  color: active(3) ? Colors.white : Colors.black,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

完整源代码

为了方便复制粘贴。

import 'dart:math' as math;

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

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        scaffoldBackgroundColor: Color(0xffd3ccca),
        primaryColor: Color(0xff86736c),
        accentColor: Color(0xff76504e),
      ),
      title: 'Flutter Demo',
      home: Scaffold(
        body: MyContent(),
        bottomNavigationBar: MyBottomNavigationBar(),
        floatingActionButton: FloatingActionButton(
          mini: true,
          onPressed: () => print('Adding new task...'),
          child: Icon(Icons.add),
        ),
        floatingActionButtonLocation:
            FloatingActionButtonLocation.miniCenterDocked,
      ),
    ),
  );
}

class MyContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text('Does this work for you?'));
  }
}

class MyBottomNavigationBar extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final _currentIndex = useState(1);
    void navigateTo(int index) => _currentIndex.value = index;
    bool active(int index) => _currentIndex.value == index;

    return BottomAppBar(
      color: Theme.of(context).primaryColor,
      shape: CustomNotchedShape(),
      child: Container(
        height: 50,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20.0),
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 12.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                IconButton(
                  onPressed: () => navigateTo(0),
                  icon: Icon(Icons.home_outlined),
                  color: active(0) ? Colors.white : Colors.black,
                ),
                IconButton(
                  onPressed: () => navigateTo(1),
                  icon: Icon(Icons.horizontal_split_outlined),
                  color: active(1) ? Colors.white : Colors.black,
                ),
                Padding(
                  padding: EdgeInsets.only(top: 24.0),
                  child: Text('New task'),
                ),
                IconButton(
                  onPressed: () => navigateTo(2),
                  icon: Icon(Icons.access_time_outlined),
                  color: active(2) ? Colors.white : Colors.black,
                ),
                IconButton(
                  onPressed: () => navigateTo(3),
                  icon: Icon(Icons.settings_outlined),
                  color: active(3) ? Colors.white : Colors.black,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class CustomNotchedShape extends NotchedShape {
  const CustomNotchedShape();

  @override
  Path getOuterPath(Rect host, Rect guest) {
    final x = math.min(host.width / 20, host.height / 3);
    final guestMargin = guest == null ? 0 : 1.0;
    if (guest == null || !host.overlaps(guest)) {
      guest = Rect.fromCenter(
          center: Offset(host.width / 2, host.top), width: 0, height: 0);
    }

    num degToRad(num deg) => deg * (math.pi / 180.0);

    return Path()
      ..moveTo(host.left, host.bottom)
      ..quadraticBezierTo(
          host.left + x, host.bottom, host.left + x, host.bottom - x)
      ..lineTo(host.left + x, host.top + x)
      ..quadraticBezierTo(host.left + x, host.top, host.left + 2 * x, host.top)
      ..lineTo(guest.left, host.top)
      ..arcTo(
          Rect.fromCenter(
              center: guest.center,
              width: guest.width + 2 * guestMargin,
              height: guest.width + 2 * guestMargin),
          degToRad(180),
          degToRad(-180),
          false)
      ..lineTo(host.right - 2 * x, host.top)
      ..quadraticBezierTo(
          host.right - x, host.top, host.right - x, host.top + x)
      ..lineTo(host.right - x, host.bottom - x)
      ..quadraticBezierTo(host.right - x, host.bottom, host.right, host.bottom)
      ..close();
  }
}