I have tried some packages and stack with image but it is not working for me. Any help would be great.
Background image with curve is not working.
Does this work for you?
You should be able to do that with a custom 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();
}
}
Then, you use this CustomNotchedShape
as the shape
of your BottomAppBar
:
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,
),
],
),
),
),
),
);
}
}
For easy copy-paste.
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();
}
}