Как сделать плавающий BottomAppBar?

Я работаю в приложении флаттера. Но мне трудно работать с пользовательским интерфейсом Flutter, потому что он не совсем соответствует пользовательскому интерфейсу.

Это дизайн пользовательского интерфейса.

И это текущий пользовательский интерфейс флаттера.

Это мой код.

      Widget buildBottomBar() {
    return BottomAppBar(
      elevation: 0.0,
      shape: const CircularNotchedRectangle(),
      notchMargin: 5.0,
      clipBehavior: Clip.antiAlias,
      child: Container(
        decoration: BoxDecoration(
          border: Border.all(
            width: 10,
            color: Colors.transparent,
          ),
        ),
        child: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          iconSize: 28,
          elevation: 0.0,
          currentIndex: selectedIndex,
          selectedLabelStyle: robotoStyle(FontWeight.w500,
              const Color.fromARGB(255, 49, 48, 54), null, null),
          showUnselectedLabels: true,
          unselectedLabelStyle:
              robotoStyle(FontWeight.w500, const Color(0xff313036), null, null),
          unselectedItemColor: const Color(0xff313036),
          selectedItemColor: const Color(0xff313036),
          selectedIconTheme: const IconThemeData(color: Color(0xffff2323)),
          onTap: onBottomBarButtonTapped,
          backgroundColor: Colors.transparent,
          items: const <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              activeIcon: Icon(
                Icons.signal_cellular_alt_rounded,
                color: Color(0xffff2323),
              ),
              icon: Icon(
                Icons.signal_cellular_alt_rounded,
                color: Color(0xff313036),
              ),
              label: "Trend",
            ),
            BottomNavigationBarItem(
              activeIcon: Icon(
                CupertinoIcons.calendar,
                color: Color(0xffff2323),
              ),
              icon: Icon(
                CupertinoIcons.calendar,
                color: Color(0xff313036),
              ),
              label: "Calendar",
            ),
            BottomNavigationBarItem(
              activeIcon: Icon(
                Icons.search_rounded,
                color: Colors.transparent,
              ),
              icon: Icon(
                Icons.search_rounded,
                color: Colors.transparent,
              ),
              label: "",
            ),
            BottomNavigationBarItem(
              activeIcon: Icon(
                CupertinoIcons.arrow_2_circlepath,
                color: Color(0xffff2323),
              ),
              icon: Icon(
                CupertinoIcons.arrow_2_circlepath,
                color: Color(0xff313036),
              ),
              label: "Restock",
            ),
            BottomNavigationBarItem(
              activeIcon: Icon(
                Icons.account_circle,
                color: Color(0xffff2323),
              ),
              icon: Icon(
                Icons.account_circle,
                color: Color(0xff313036),
              ),
              label: "Account",
            ),
          ],
        ),
      ),
      // ),
    );
  }

 Widget build(BuildContext context) {
    return Scaffold(
      extendBody: true,
      resizeToAvoidBottomInset: false,
      appBar: null,
      backgroundColor: Colors.white,
      body: ...,
      bottomNavigationBar: buildBottomBar(),
      floatingActionButton: Padding(
        padding: const EdgeInsets.all(5.0),
        child: FloatingActionButton(...)
      ),
  }

И следующая проблема — это как раз радиус под плавающей кнопкой поиска. Как применить радиус границы, как этот дизайн?

Дайте мне знать, как это сделать как можно скорее.

Вы не можете сделать так много изогнутых, но есть пакет curved_navigation_bar, который делает то, что вам нравится, но проблема в том, что вы не можете сделать фиксированное плавание

mohammad esmaili 04.10.2022 03:01

Привет, @mohammadesmaili. Любые другие альтернативы? Это сводит меня с ума.

Giant Brain 04.10.2022 03:11

А как сделать отступ вокруг нижнего AppBar'а?

Giant Brain 04.10.2022 03:27
1
3
160
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я решил это, настроив стандартный пакет. Причина этому явление возникает из-за того, что край смещается вправо применяется Margin или Padding. То есть решение состоит в том, чтобы переместить клипера влево или вправо в зависимости от примененного поля или заполнения. Затем я ниже будет представлено пошаговое решение.

  1. Нажмите Ctrl+B (для Android Studio) или F12 (для VS Code) на BottomAppBar().
  1. Нажмите целевую кнопку.

  1. Скопируйте файл bottom_app_bar.dart в каталог пользовательских библиотек, затем переименуйте его в custom_bottom_app_bar.dart.

  1. Измените часть кода в новом файле custom_bottom_app_bar.dart.

  1. Все сделано. Наслаждайтесь новым виджетом.
  1. Ниже я добавлю полный пример кода.

custom_bottom_app_bar.dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.*

import 'package:flutter/foundation.dart';
// import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';

// import 'bottom_app_bar_theme.dart';
// import 'elevation_overlay.dart';
// import 'package:flutter/material.dart';
// import 'scaffold.dart';
// import 'theme.dart';

// Examples can assume:
// late Widget bottomAppBarContents;

/// A container that is typically used with [Scaffold.bottomNavigationBar], and
/// can have a notch along the top that makes room for an overlapping
/// [FloatingActionButton].
///
/// Typically used with a [Scaffold] and a [FloatingActionButton].
///
/// {@tool snippet}
/// ```dart
/// Scaffold(
///   bottomNavigationBar: CustomBottomAppBar(
///     color: Colors.white,
///     child: bottomAppBarContents,
///   ),
///   floatingActionButton: const FloatingActionButton(onPressed: null),
/// )
/// ```
/// {@end-tool}
///
/// {@tool dartpad}
/// This example shows the [CustomBottomAppBar], which can be configured to have a notch using the
/// [CustomBottomAppBar.shape] property. This also includes an optional [FloatingActionButton], which illustrates
/// the [FloatingActionButtonLocation]s in relation to the [CustomBottomAppBar].
///
/// ** See code in examples/api/lib/material/bottom_app_bar/bottom_app_bar.1.dart **
/// {@end-tool}
///
/// See also:
///
///  * [NotchedShape] which calculates the notch for a notched [CustomBottomAppBar].
///  * [FloatingActionButton] which the [CustomBottomAppBar] makes a notch for.
///  * [AppBar] for a toolbar that is shown at the top of the screen.
class CustomBottomAppBar extends StatefulWidget {
  /// Creates a bottom application bar.
  ///
  /// The [clipBehavior] argument defaults to [Clip.none] and must not be null.
  /// Additionally, [elevation] must be non-negative.
  ///
  /// If [color], [elevation], or [shape] are null, their [BottomAppBarTheme] values will be used.
  /// If the corresponding [BottomAppBarTheme] property is null, then the default
  /// specified in the property's documentation will be used.
  const CustomBottomAppBar({
    super.key,
    this.color,
    this.elevation,
    this.shape,
    this.clipBehavior = Clip.none,
    this.notchMargin = 4.0,
    this.child,
    this.positionInHorizontal = 0.0,
  }) : assert(elevation == null || elevation >= 0.0);

  /// The widget below this widget in the tree.
  ///
  /// {@macro flutter.widgets.ProxyWidget.child}
  ///
  /// Typically this the child will be a [Row], with the first child
  /// being an [IconButton] with the [Icons.menu] icon.
  final Widget? child;

  /// The bottom app bar's background color.
  ///
  /// If this property is null then [BottomAppBarTheme.color] of
  /// [ThemeData.bottomAppBarTheme] is used. If that's null then
  /// [ThemeData.bottomAppBarColor] is used.
  final Color? color;

  /// The z-coordinate at which to place this bottom app bar relative to its
  /// parent.
  ///
  /// This controls the size of the shadow below the bottom app bar. The
  /// value is non-negative.
  ///
  /// If this property is null then [BottomAppBarTheme.elevation] of
  /// [ThemeData.bottomAppBarTheme] is used. If that's null, the default value
  /// is 8.
  final double? elevation;

  /// The notch that is made for the floating action button.
  ///
  /// If this property is null then [BottomAppBarTheme.shape] of
  /// [ThemeData.bottomAppBarTheme] is used. If that's null then the shape will
  /// be rectangular with no notch.
  final NotchedShape? shape;

  /// {@macro flutter.material.Material.clipBehavior}
  ///
  /// Defaults to [Clip.none], and must not be null.
  final Clip clipBehavior;

  /// The margin between the [FloatingActionButton] and the [CustomBottomAppBar]'s
  /// notch.
  ///
  /// Not used if [shape] is null.
  final double notchMargin;

  final double positionInHorizontal;

  @override
  State createState() => _CustomBottomAppBarState();
}

class _CustomBottomAppBarState extends State<CustomBottomAppBar> {
  late ValueListenable<ScaffoldGeometry> geometryListenable;
  final GlobalKey materialKey = GlobalKey();
  static const double _defaultElevation = 8.0;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    geometryListenable = Scaffold.geometryOf(context);
  }

  @override
  Widget build(BuildContext context) {
    final BottomAppBarTheme babTheme = BottomAppBarTheme.of(context);
    final bool hasFab = Scaffold.of(context).hasFloatingActionButton;
    final NotchedShape? notchedShape = widget.shape ?? babTheme.shape;
    final CustomClipper<Path> clipper = notchedShape != null && hasFab
        ? _BottomAppBarClipper(
            geometry: geometryListenable,
            shape: notchedShape,
            materialKey: materialKey,
            notchMargin: widget.notchMargin,
            position2MoveHorizontal: widget.positionInHorizontal,
          )
        : const ShapeBorderClipper(shape: RoundedRectangleBorder());
    final double elevation =
        widget.elevation ?? babTheme.elevation ?? _defaultElevation;
    final Color color =
        widget.color ?? babTheme.color ?? Theme.of(context).bottomAppBarColor;
    final Color effectiveColor =
        ElevationOverlay.applyOverlay(context, color, elevation);
    return PhysicalShape(
      clipper: clipper,
      elevation: elevation,
      color: effectiveColor,
      clipBehavior: widget.clipBehavior,
      child: Material(
        key: materialKey,
        type: MaterialType.transparency,
        child: widget.child == null ? null : SafeArea(child: widget.child!),
      ),
    );
  }
}

class _BottomAppBarClipper extends CustomClipper<Path> {
  const _BottomAppBarClipper({
    required this.geometry,
    required this.shape,
    required this.materialKey,
    required this.notchMargin,
    required this.position2MoveHorizontal,
  }) : super(reclip: geometry);

  final ValueListenable<ScaffoldGeometry> geometry;
  final NotchedShape shape;
  final GlobalKey materialKey;
  final double notchMargin;
  final double position2MoveHorizontal;

  // Returns the top of the CustomBottomAppBar in global coordinates.
  //
  // If the Scaffold's bottomNavigationBar was specified, then we can use its
  // geometry value, otherwise we compute the location based on the AppBar's
  // Material widget.
  double get bottomNavigationBarTop {
    final double? bottomNavigationBarTop =
        geometry.value.bottomNavigationBarTop;
    if (bottomNavigationBarTop != null) {
      return bottomNavigationBarTop;
    }
    final RenderBox? box =
        materialKey.currentContext?.findRenderObject() as RenderBox?;
    return box?.localToGlobal(Offset.zero).dy ?? 0;
  }

  @override
  Path getClip(Size size) {
    // button is the floating action button's bounding rectangle in the
    // coordinate system whose origin is at the appBar's top left corner,
    // or null if there is no floating action button.

    final Rect? button = geometry.value.floatingActionButtonArea
        ?.translate(position2MoveHorizontal, bottomNavigationBarTop * -1.0);
    return shape.getOuterPath(Offset.zero & size, button?.inflate(notchMargin));
  }

  @override
  bool shouldReclip(_BottomAppBarClipper oldClipper) {
    return oldClipper.geometry != geometry ||
        oldClipper.shape != shape ||
        oldClipper.notchMargin != notchMargin;
  }
}

дом.дарт

Widget buildBottomBar() {
    Map bottomAppBarMargin = {
      'left': 20.0,
      'right': 20.0,
      'bottom': 12.0,
      'top': 0.0,
    };
    return Container(
      margin: EdgeInsets.only(
        left: bottomAppBarMargin['left'],
        right: bottomAppBarMargin['right'],
        bottom: bottomAppBarMargin['bottom'],
        top: bottomAppBarMargin['top'],
      ),
      child: CustomBottomAppBar(
        elevation: 0.0,
        positionInHorizontal: (0.0-bottomAppBarMargin['left']),
        color: Colors.white,
        shape: const CircularNotchedRectangle(),
        notchMargin: 5.0,
        clipBehavior: Clip.antiAlias,
        child: SizedBox(...)
      ),
    );
}

Результат

Другие вопросы по теме