参考链接:
https://gallery.flutter.cn/#/
底部应用栏demo
重点看凹口设置:
floatingActionButtonLocation
bottomNavigationBar
BottomAppBar
的 shape: CircularNotchedRectangle
,这个CircularNotchedRectangle
是必须配置的。
同时需设置 floatingActionButtonLocation
这个参数接受的是
FloatingActionButtonLocation
提供了
startTop
、startFloat
、centerFloat
、endFloat
… 其他停靠的只有这三个属性startDocked
、centerDocked
、endDocked
。
如果需要自己计算位置可以继承StandardFabLocation
重写 getOffsetX 、getOffsetY。
计算值可以参考系统提供的的 FabEndOffsetX 、FabDockedOffsetY几个位置的计算:
mixin FabEndOffsetX on StandardFabLocation {
/// Calculates x-offset for end-aligned [FloatingActionButtonLocation]s.
@override
double getOffsetX(ScaffoldPrelayoutGeometry scaffoldGeometry, double adjustment) {
assert(scaffoldGeometry.textDirection != null);
switch (scaffoldGeometry.textDirection) {
case TextDirection.rtl:
return StandardFabLocation._leftOffsetX(scaffoldGeometry, adjustment);
case TextDirection.ltr:
return StandardFabLocation._rightOffsetX(scaffoldGeometry, adjustment);
}
}
}
下面是官方源码修改的demo
// Copyright 2019 The Flutter team. 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/cupertino.dart';
import 'package:flutter/material.dart';
class BottomAppBarDemo extends StatefulWidget {
const BottomAppBarDemo({
Key? key}) : super(key: key);
State createState() => _BottomAppBarDemoState();
}
class _BottomAppBarDemoState extends State<BottomAppBarDemo>
with RestorationMixin {
final RestorableBool _showFab = RestorableBool(true);
final RestorableBool _showNotch = RestorableBool(true);
final RestorableInt _currentFabLocation = RestorableInt(0);
DateTime date = DateTime.now();
String get restorationId => 'bottom_app_bar_demo';
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_showFab, 'show_fab');
registerForRestoration(_showNotch, 'show_notch');
registerForRestoration(_currentFabLocation, 'fab_location');
}
void dispose() {
_showFab.dispose();
_showNotch.dispose();
_currentFabLocation.dispose();
super.dispose();
}
// Since FloatingActionButtonLocation is not an enum, the index of the
// selected FloatingActionButtonLocation is used for state restoration.
static const List<FloatingActionButtonLocation> _fabLocations = [
FloatingActionButtonLocation.endDocked,
FloatingActionButtonLocation.centerDocked,
FloatingActionButtonLocation.endFloat,
FloatingActionButtonLocation.centerFloat,
];
void _onShowNotchChanged(bool value) {
setState(() {
_showNotch.value = value;
});
}
void _onShowFabChanged(bool value) {
setState(() {
_showFab.value = value;
});
}
void _onFabLocationChanged(int? value) {
setState(() {
_currentFabLocation.value = value!;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text("底部應用欄"),
),
body: ListView(
padding: const EdgeInsets.only(bottom: 88),
children: [
SwitchListTile(
title: Text(
"悬浮操作按钮",
),
value: _showFab.value,
onChanged: _onShowFabChanged,
),
SwitchListTile(
title: Text("凹口"),
value: _showNotch.value,
onChanged: _onShowNotchChanged,
),
Padding(
padding: const EdgeInsets.all(16),
child: Text("悬浮操作按钮位置"),
),
RadioListTile<int>(
title: Text(
"停靠-末端",
),
value: 0,
groupValue: _currentFabLocation.value,
onChanged: _onFabLocationChanged,
),
RadioListTile<int>(
title: Text(
"停靠-居中",
),
value: 1,
groupValue: _currentFabLocation.value,
onChanged: _onFabLocationChanged,
),
RadioListTile<int>(
title: Text(
"悬浮-末端",
),
value: 2,
groupValue: _currentFabLocation.value,
onChanged: _onFabLocationChanged,
),
RadioListTile<int>(
title: Text(
"悬浮-居中",
),
value: 3,
groupValue: _currentFabLocation.value,
onChanged: _onFabLocationChanged,
),
],
),
floatingActionButton: _showFab.value
? FloatingActionButton(
onPressed: () {
_buildCupertinoDatePicker(
context,
_BottomPicker(
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.date,
onDateTimeChanged: (newDateTime) {
setState(() => date = newDateTime);
},
),
));
},
tooltip: "创建按钮",
child: const Icon(Icons.add),
)
: null,
floatingActionButtonLocation: _fabLocations[_currentFabLocation.value],
bottomNavigationBar: _DemoBottomAppBar(
fabLocation: _fabLocations[_currentFabLocation.value],
shape: _showNotch.value ? const CircularNotchedRectangle() : null,
),
);
}
void _buildCupertinoDatePicker(BuildContext context, Widget child) {
final themeData = CupertinoTheme.of(context);
final dialogBody = CupertinoTheme(
data: themeData,
child: child,
);
showCupertinoModalPopup<void>(
context: context,
builder: (context) => dialogBody,
);
}
}
class _BottomPicker extends StatelessWidget {
const _BottomPicker({
Key? key,
required this.child,
}) : super(key: key);
final Widget child;
Widget build(BuildContext context) {
return Container(
height: 216,
padding: const EdgeInsets.only(top: 6),
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
color: CupertinoColors.systemBackground.resolveFrom(context),
child: DefaultTextStyle(
style: TextStyle(
color: CupertinoColors.label.resolveFrom(context),
fontSize: 22,
),
child: GestureDetector(
// Blocks taps from propagating to the modal sheet and popping.
onTap: () {
},
child: SafeArea(
top: false,
child: child,
),
),
),
);
}
}
class _DemoBottomAppBar extends StatelessWidget {
const _DemoBottomAppBar({
required this.fabLocation,
this.shape,
});
final FloatingActionButtonLocation fabLocation;
final NotchedShape? shape;
static final centerLocations = <FloatingActionButtonLocation>[
FloatingActionButtonLocation.centerDocked,
FloatingActionButtonLocation.centerFloat,
];
Widget build(BuildContext context) {
return BottomAppBar(
shape: shape,
color: Colors.green,
child: IconTheme(
data: IconThemeData(color: Theme.of(context).colorScheme.onPrimary),
child: Row(
children: [
IconButton(
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
icon: const Icon(Icons.menu),
onPressed: () {
},
),
if (centerLocations.contains(fabLocation)) const Spacer(),
IconButton(
tooltip: "starterAppTooltipSearch",
icon: const Icon(Icons.search),
onPressed: () {
},
),
IconButton(
tooltip: "starterAppTooltipFavorite",
icon: const Icon(Icons.favorite),
onPressed: () {
},
),
],
),
),
);
}
}