Adobe XD to flutter code results in "RenderCustomMultiChildLayoutBox object was given an infinite size during layout." error - flutter

I have started to use Adobe XD to design a UI for my flutter application. When I export my UI to flutter code and try to add it with my existing code I get the error
"RenderCustomMultiChildLayoutBox object was given an infinite size during layout."
I am calling the XDOutlet() function in an already working layout, but when the code runes that error is present.
I have not changed anything from the exported code, and I can't seem to pinpoint the issue. Any help will be appreciated!
XDOutlet.dart
class XDOutlet extends StatelessWidget {
XDOutlet({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xffffffff),
body: Stack(
children: <Widget>[
Transform.translate(
offset: Offset(114.0, 315.2),
child:
// Adobe XD layer: 'Bottom_Outlet' (component)
SizedBox(
width: 200.0,
height: 200.0,
child: XDBottom_Outlet(),
),
),
Transform.translate(
offset: Offset(114.0, 52.0),
child:
// Adobe XD layer: 'Top_Outlet' (component)
SizedBox(
width: 200.0,
height: 200.0,
child: XDTop_Outlet(),
),
),
Transform.translate(
offset: Offset(220.0, 809.0),
child:
// Adobe XD layer: 'Restart' (component)
SizedBox(
width: 188.0,
height: 60.0,
child: XDRestart(),
),
),
Transform.translate(
offset: Offset(20.0, 809.0),
child:
// Adobe XD layer: 'Update' (component)
SizedBox(
width: 188.0,
height: 60.0,
child: XDUpdate(),
),
),
Transform.translate(
offset: Offset(20.0, 727.0),
child:
// Adobe XD layer: 'Statistics' (component)
PageLink(
links: [
PageLinkInfo(
transition: LinkTransition.SlideUp,
ease: Curves.easeOut,
duration: 0.3,
pageBuilder: () => XDOurlet_Statistics(),
),
],
child: SizedBox(
width: 388.0,
height: 60.0,
child: XDStatistics(),
),
),
),
Transform.translate(
offset: Offset(162.7, 578.4),
child:
// Adobe XD layer: 'All_Power' (component)
SizedBox(
width: 103.0,
height: 103.0,
child: XDAll_Power(),
),
),
],
),
);
}
}
XDBottom_Outlet.dart
class XDBottom_Outlet extends StatelessWidget {
final VoidCallback BottomOutlet;
XDBottom_Outlet({
Key key,
this.BottomOutlet,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => BottomOutlet?.call(),
child: Stack(
children: <Widget>[
Pinned.fromSize(
bounds: Rect.fromLTWH(0.0, 0.0, 200.0, 200.0),
size: Size(200.0, 200.0),
pinLeft: true,
pinRight: true,
pinTop: true,
pinBottom: true,
child:
// Adobe XD layer: 'Bottom_Outlet' (shape)
SvgPicture.string(
_svg_p3l2i1,
allowDrawingOutsideViewBox: true,
fit: BoxFit.fill,
),
),
],
),
);
}
}
const String _svg_p3l2i1 =
'<svg viewBox="0.0 0.0 200.0 200.0" ><defs><filter id="shadow"><feDropShadow dx="0" dy="3" stdDeviation="6"/></filter></defs><path transform="translate(-2.25, -2.25)" d="M 152.8860168457031 2.25 L 51.52471542358398 2.25 C 24.34328460693359 2.25 2.25 24.83424758911133 2.25 52.50664138793945 L 2.25 151.94873046875 C 2.25 179.6211242675781 24.34328460693359 202.2053680419922 51.52471542358398 202.2053680419922 L 152.9306488037109 202.2053680419922 C 180.1120910644531 202.2053680419922 202.25 179.6211242675781 202.25 151.94873046875 L 202.25 52.50664138793945 C 202.2053680419922 24.83424758911133 180.1120910644531 2.25 152.8860168457031 2.25 Z M 66.07504272460938 99.99604797363281 L 66.07504272460938 100.308479309082 C 66.07504272460938 105.173469543457 62.01344680786133 109.4135971069336 57.19309616088867 109.4135971069336 L 46.79363250732422 109.4135971069336 C 41.97328186035156 109.4135971069336 37.91168975830078 105.173469543457 37.91168975830078 100.308479309082 L 37.91168975830078 99.99604797363281 L 37.91168975830078 54.69365310668945 L 37.91168975830078 54.06879043579102 C 38.35801696777344 49.24844360351563 42.01791763305664 45.05294418334961 46.79363250732422 45.05294418334961 L 57.19309616088867 45.05294418334961 C 62.05807876586914 45.05294418334961 66.07504272460938 49.42696762084961 66.07504272460938 54.33658218383789 L 66.07504272460938 99.99604797363281 Z M 120.5271835327148 165.7849273681641 C 120.5271835327148 170.8730926513672 116.5548477172852 173.6403198242188 111.5559692382813 173.6403198242188 L 92.89942169189453 173.6403198242188 C 87.90053558349609 173.6403198242188 83.92820739746094 170.0250549316406 83.92820739746094 164.8922729492188 L 83.92820739746094 151.8148345947266 C 83.92820739746094 141.4153747558594 92.00676727294922 132.9351196289063 102.2276916503906 132.9351196289063 C 112.4486312866211 132.9351196289063 120.5271835327148 141.4153747558594 120.5271835327148 151.8148345947266 L 120.5271835327148 165.7849273681641 Z M 166.4990539550781 99.99604797363281 L 166.4990539550781 100.308479309082 C 166.4990539550781 105.173469543457 162.4374694824219 109.4135971069336 157.6171112060547 109.4135971069336 L 146.7713165283203 109.4135971069336 C 141.9509582519531 109.4135971069336 137.8893585205078 105.173469543457 137.8893585205078 100.308479309082 L 137.8893585205078 99.99604797363281 L 137.8893585205078 54.69365310668945 L 137.8893585205078 54.06879043579102 C 138.335693359375 49.24844360351563 141.9955902099609 45.05294418334961 146.7713165283203 45.05294418334961 L 157.6171112060547 45.05294418334961 C 162.4374542236328 45.05294418334961 166.4990539550781 49.42696762084961 166.4990539550781 54.33658218383789 L 166.4990539550781 99.99604797363281 Z" fill="#d90b0b" stroke="none" stroke-width="1" stroke-miterlimit="4" stroke-linecap="butt" filter="url(#shadow)"/></svg>';
XDTop_Outlet.dart
class XDTop_Outlet extends StatelessWidget {
final VoidCallback TopOutlet;
XDTop_Outlet({
Key key,
this.TopOutlet,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => TopOutlet?.call(),
child: Stack(
children: <Widget>[
Pinned.fromSize(
bounds: Rect.fromLTWH(0.0, 0.0, 200.0, 200.0),
size: Size(200.0, 200.0),
pinLeft: true,
pinRight: true,
pinTop: true,
pinBottom: true,
child:
// Adobe XD layer: 'Top_Outlet' (shape)
SvgPicture.string(
_svg_p3l2i1,
allowDrawingOutsideViewBox: true,
fit: BoxFit.fill,
),
),
],
),
);
}
}
const String _svg_p3l2i1 =
'<svg viewBox="0.0 0.0 200.0 200.0" ><defs><filter id="shadow"><feDropShadow dx="0" dy="3" stdDeviation="6"/></filter></defs><path transform="translate(-2.25, -2.25)" d="M 152.8860168457031 2.25 L 51.52471542358398 2.25 C 24.34328460693359 2.25 2.25 24.83424758911133 2.25 52.50664138793945 L 2.25 151.94873046875 C 2.25 179.6211242675781 24.34328460693359 202.2053680419922 51.52471542358398 202.2053680419922 L 152.9306488037109 202.2053680419922 C 180.1120910644531 202.2053680419922 202.25 179.6211242675781 202.25 151.94873046875 L 202.25 52.50664138793945 C 202.2053680419922 24.83424758911133 180.1120910644531 2.25 152.8860168457031 2.25 Z M 66.07504272460938 99.99604797363281 L 66.07504272460938 100.308479309082 C 66.07504272460938 105.173469543457 62.01344680786133 109.4135971069336 57.19309616088867 109.4135971069336 L 46.79363250732422 109.4135971069336 C 41.97328186035156 109.4135971069336 37.91168975830078 105.173469543457 37.91168975830078 100.308479309082 L 37.91168975830078 99.99604797363281 L 37.91168975830078 54.69365310668945 L 37.91168975830078 54.06879043579102 C 38.35801696777344 49.24844360351563 42.01791763305664 45.05294418334961 46.79363250732422 45.05294418334961 L 57.19309616088867 45.05294418334961 C 62.05807876586914 45.05294418334961 66.07504272460938 49.42696762084961 66.07504272460938 54.33658218383789 L 66.07504272460938 99.99604797363281 Z M 120.5271835327148 165.7849273681641 C 120.5271835327148 170.8730926513672 116.5548477172852 173.6403198242188 111.5559692382813 173.6403198242188 L 92.89942169189453 173.6403198242188 C 87.90053558349609 173.6403198242188 83.92820739746094 170.0250549316406 83.92820739746094 164.8922729492188 L 83.92820739746094 151.8148345947266 C 83.92820739746094 141.4153747558594 92.00676727294922 132.9351196289063 102.2276916503906 132.9351196289063 C 112.4486312866211 132.9351196289063 120.5271835327148 141.4153747558594 120.5271835327148 151.8148345947266 L 120.5271835327148 165.7849273681641 Z M 166.4990539550781 99.99604797363281 L 166.4990539550781 100.308479309082 C 166.4990539550781 105.173469543457 162.4374694824219 109.4135971069336 157.6171112060547 109.4135971069336 L 146.7713165283203 109.4135971069336 C 141.9509582519531 109.4135971069336 137.8893585205078 105.173469543457 137.8893585205078 100.308479309082 L 137.8893585205078 99.99604797363281 L 137.8893585205078 54.69365310668945 L 137.8893585205078 54.06879043579102 C 138.335693359375 49.24844360351563 141.9955902099609 45.05294418334961 146.7713165283203 45.05294418334961 L 157.6171112060547 45.05294418334961 C 162.4374542236328 45.05294418334961 166.4990539550781 49.42696762084961 166.4990539550781 54.33658218383789 L 166.4990539550781 99.99604797363281 Z" fill="#d90b0b" stroke="none" stroke-width="1" stroke-miterlimit="4" stroke-linecap="butt" filter="url(#shadow)"/></svg>';
XDRestart.dart
class XDRestart extends StatelessWidget {
final VoidCallback Restart;
XDRestart({
Key key,
this.Restart,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => Restart?.call(),
child: Stack(
children: <Widget>[
Pinned.fromSize(
bounds: Rect.fromLTWH(0.0, 0.0, 188.0, 60.0),
size: Size(188.0, 60.0),
pinLeft: true,
pinRight: true,
pinTop: true,
pinBottom: true,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: const Color(0xfffa0000),
boxShadow: [
BoxShadow(
color: const Color(0x29000000),
offset: Offset(0, 3),
blurRadius: 6,
),
],
),
),
),
Pinned.fromSize(
bounds: Rect.fromLTWH(27.0, 7.0, 134.0, 47.0),
size: Size(188.0, 60.0),
pinLeft: true,
pinRight: true,
pinTop: true,
pinBottom: true,
child:
// Adobe XD layer: 'Restart' (text)
Text(
'Restart',
style: TextStyle(
fontFamily: 'Raleway',
fontSize: 40,
color: const Color(0xffffffff),
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.left,
),
),
],
),
);
}
}
XDUpdate.dart
class XDUpdate extends StatelessWidget {
final VoidCallback Update;
XDUpdate({
Key key,
this.Update,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => Update?.call(),
child: Stack(
children: <Widget>[
Pinned.fromSize(
bounds: Rect.fromLTWH(0.0, 0.0, 188.0, 60.0),
size: Size(188.0, 60.0),
pinLeft: true,
pinRight: true,
pinTop: true,
pinBottom: true,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: const Color(0xff3790f7),
boxShadow: [
BoxShadow(
color: const Color(0x29000000),
offset: Offset(0, 3),
blurRadius: 6,
),
],
),
),
),
Pinned.fromSize(
bounds: Rect.fromLTWH(24.0, 7.0, 139.0, 47.0),
size: Size(188.0, 60.0),
pinLeft: true,
pinRight: true,
pinTop: true,
pinBottom: true,
child: Text(
'Update',
style: TextStyle(
fontFamily: 'Raleway',
fontSize: 40,
color: const Color(0xffffffff),
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.left,
),
),
],
),
);
}
}
XDStatistics.dart
class XDStatistics extends StatelessWidget {
final VoidCallback StatisticsPage;
XDStatistics({
Key key,
this.StatisticsPage,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => StatisticsPage?.call(),
child: Stack(
children: <Widget>[
Pinned.fromSize(
bounds: Rect.fromLTWH(0.0, 0.0, 388.0, 60.0),
size: Size(388.0, 60.0),
pinLeft: true,
pinRight: true,
pinTop: true,
pinBottom: true,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: const Color(0xff2cd625),
boxShadow: [
BoxShadow(
color: const Color(0x29000000),
offset: Offset(0, 3),
blurRadius: 6,
),
],
),
),
),
Pinned.fromSize(
bounds: Rect.fromLTWH(111.0, 7.0, 168.0, 47.0),
size: Size(388.0, 60.0),
pinTop: true,
pinBottom: true,
fixedWidth: true,
child:
// Adobe XD layer: 'Statistics' (text)
Text(
'Statistics',
style: TextStyle(
fontFamily: 'Raleway',
fontSize: 40,
color: const Color(0xffffffff),
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.left,
),
),
],
),
);
}
}
XDAll_Power.dart
class XDAll_Power extends StatelessWidget {
final VoidCallback AllPower;
XDAll_Power({
Key key,
this.AllPower,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => AllPower?.call(),
child: Stack(
children: <Widget>[
Pinned.fromSize(
bounds: Rect.fromLTWH(0.0, 0.0, 102.6, 102.6),
size: Size(102.6, 102.6),
pinLeft: true,
pinRight: true,
pinTop: true,
pinBottom: true,
child:
// Adobe XD layer: 'Icon ionic-md-power' (shape)
SvgPicture.string(
_svg_md9vfg,
allowDrawingOutsideViewBox: true,
fit: BoxFit.fill,
),
),
],
),
);
}
}
const String _svg_md9vfg =
'<svg viewBox="0.0 0.0 102.6 102.6" ><defs><filter id="shadow"><feDropShadow dx="0" dy="3" stdDeviation="6"/></filter></defs><path transform="translate(-3.37, -3.37)" d="M 60.35555648803711 3.375000238418579 L 48.96437835693359 3.375000238418579 L 48.96437835693359 60.35555648803711 L 60.35555648803711 60.35555648803711 L 60.35555648803711 3.375000238418579 Z M 87.87191772460938 15.75243091583252 L 79.78467559814453 23.83967781066895 C 88.78420257568359 31.0886116027832 94.55375671386719 42.18391799926758 94.55375671386719 54.65997314453125 C 94.55375671386719 76.70264434814453 76.72730255126953 94.55374908447266 54.65998077392578 94.55374908447266 C 32.59264755249023 94.55374908447266 14.76618099212646 76.70264434814453 14.76618099212646 54.65997314453125 C 14.76618099212646 42.18391799926758 20.51108551025391 31.0639533996582 29.4612979888916 23.76571083068848 L 21.42336273193359 15.72777462005615 C 10.37737083435059 25.14645385742188 3.375000238418579 39.05257797241211 3.375000238418579 54.65997314453125 C 3.375000238418579 82.98998260498047 26.32995796203613 105.9449310302734 54.65997314453125 105.9449310302734 C 82.98998260498047 105.9449310302734 105.9449310302734 82.98998260498047 105.9449310302734 54.65997314453125 C 105.9449310302734 39.05257797241211 98.94255828857422 25.14645385742188 87.87191772460938 15.75243091583252 Z" fill="#d90b0b" stroke="none" stroke-width="1" stroke-miterlimit="4" stroke-linecap="butt" filter="url(#shadow)"/></svg>';

Unfortunately XD to flutter is not 100% works and not recommended, here's why :
There is not just create and convert from xd to flutter
You need to chain the widget your self
Use XD to flutter for UI purpose only, because XD cannot contain logical behavior of your program
I would highly recommend you to code it your self, flutter covering almost 100% possibilities of a custom widget, and it's easy enough to understand rather than create UI and convert it to a fully functional program which almost impossible to work flawlessly

Related

Fl Chart Flutter with date data

I want to show fl line chart using this plugin https://pub.dev/packages/fl_chart.
data is double value and date in format of yyyy-MM-dd. Now I tried to show this data in chart.
Y axis showing data correctly but date calculations are not may be correct it showing lots of dates.
and If I add minX and maxX app got stuck due to lots of dates calculation between min and max X date values.
this is my code: in this data x is Date value and Y is double value.
I got the idea from this link https://github.com/imaNNeoFighT/fl_chart/issues/471
class AnalyticsChart extends StatefulWidget {
const AnalyticsChart({Key? key}) : super(key: key);
#override
_AnalyticsChartState createState() => _AnalyticsChartState();
}
class _AnalyticsChartState extends State<AnalyticsChart> {
List<Color> gradientColors = [
const Color(0xff099f53),
const Color(0xfffefffe),
];
bool showAvg = false;
final dateList = [];
#override
Widget build(BuildContext context) {
double windowHeight = MediaQuery.of(context).size.height - 370;
return Container(
height: windowHeight,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(0),
),
color: whiteColor),
child: Padding(
padding:
const EdgeInsets.only(right: 18.0, left: 12.0, top: 15, bottom: 12),
child: BlocBuilder<AnalyticsChartBloc, AnalyticsChartState>(
builder: (BuildContext context, AnalyticsChartState state) {
if (state is LoadAnalyticsStatesState) {
if (state.totalSales != null &&
(state.totalSales?.isNotEmpty ?? false)) {
List<FlSpot>? flSpots = [];
dateList.clear();
flSpots = state.totalSales?.map((e) {
dateList.add(e.x);
return FlSpot(
DateTime.parse(e.x ?? '') // e.date = "2020-10-24"
.millisecondsSinceEpoch
.toDouble(),
double.parse(e.y ?? '0'));
}).toList();
return LineChart(
mainData(flSpots),
);
}
}
return _buildNoChart();
}),
),
);
}
Widget bottomTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
height: 1,
color: placeholderColor,
fontWeight: weight500,
fontSize: fontsize12,
);
final DateTime date = DateTime.fromMillisecondsSinceEpoch(value.toInt());
final parts = date.toIso8601String().split("T");
return SideTitleWidget(
space: 12,
child: Text(parts.first, style: style),
axisSide: meta.axisSide,
);
}
Widget leftTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
color: placeholderColor,
fontWeight: weight100,
fontSize: fontsize9,
);
return Container(
padding: const EdgeInsets.only(right: 10),
height: 50.0,
child: Text(
value.toStringAsFixed(0),
style: style,
));
}
LineChartData mainData(List<FlSpot>? flSpots) {
final List<double> xList = [];
final List<double> yList = [];
flSpots?.map((e) {
xList.add(e.x);
yList.add(e.y);
}).toList();
final minX = xList.reduce(min); //
final maxX = xList.reduce(max); //
final minY = yList.reduce(min); //
final maxY = ((yList.reduce(max)) + 1000).toInt().toDouble(); //
debugPrint('minX/maxX $minX $maxX ');
debugPrint('minY/maxY $minY $maxY ');
debugPrint('maxX-minX ${maxX - minX} ');
return LineChartData(
lineTouchData: LineTouchData(
enabled: true,
touchTooltipData: LineTouchTooltipData(
tooltipBgColor: primaryColorGreen,
tooltipRoundedRadius: 4.0,
showOnTopOfTheChartBoxArea: false,
fitInsideHorizontally: true,
getTooltipItems: (touchedSpots) {
return touchedSpots.map(
(LineBarSpot touchedSpot) {
const textStyle = TextStyle(
fontSize: fontsize13,
fontWeight: weight500,
color: whiteColor,
height: 1.4,
);
return LineTooltipItem(
'Total Sales \n 17,500',
textStyle,
);
},
).toList();
},
),
),
gridData: FlGridData(
show: true,
drawVerticalLine: true,
horizontalInterval: 1,
verticalInterval: 1,
getDrawingHorizontalLine: (value) {
return FlLine(
color: borderColor,
strokeWidth: 1,
);
},
getDrawingVerticalLine: (value) {
return FlLine(
color: Colors.transparent,
strokeWidth: 1,
);
},
),
titlesData: FlTitlesData(
show: true,
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: (flSpots![flSpots.length - 1].x - flSpots[0].x),
getTitlesWidget: bottomTitleWidgets,
))),
maxX: maxX,
minX: minX,
maxY: maxY,
minY: minY,
borderData: FlBorderData(
show: true, border: Border.all(color: Colors.transparent, width: 0)),
lineBarsData: [
LineChartBarData(
spots: flSpots.asMap().entries.map((e) {
return FlSpot(e.key.toDouble(), e.value.y);
}).toList(),
isCurved: false,
color: primaryColorGreen,
barWidth: 2,
isStrokeCapRound: true,
dotData: FlDotData(
show: true,
),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: gradientColors
.map((color) => color.withOpacity(0.3))
.toList(),
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
),
],
);
}
Widget _buildNoChart() => const SizedBox(
child: Center(
child: Text('No chart data found'),
),
);
}
///////////////////////

How to create this type of bottom navigation bar with curved background? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
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,
),
],
),
),
),
),
);
}
}
Full source code
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();
}
}

How to make custom Bottom Navigation Bar in Flutter

I have a design of Bottom Navigation Bar like this:
I try my best to create custom of Bottom Navigation Bar with this code:
class FABBottomAppBarItem {
FABBottomAppBarItem({this.iconData, this.text});
IconData iconData;
String text;
}
class FABBottomAppBar extends StatefulWidget {
FABBottomAppBar({
this.items,
this.centerItemText,
this.height: 60.0,
this.iconSize: 24.0,
this.backgroundColor,
this.color,
this.selectedColor,
this.notchedShape,
this.onTabSelected,
}) {
assert(this.items.length == 2 || this.items.length == 4);
}
final List<FABBottomAppBarItem> items;
final String centerItemText;
final double height;
final double iconSize;
final Color backgroundColor;
final Color color;
final Color selectedColor;
final NotchedShape notchedShape;
final ValueChanged<int> onTabSelected;
#override
State<StatefulWidget> createState() => FABBottomAppBarState();
}
class FABBottomAppBarState extends State<FABBottomAppBar> {
int _selectedIndex = 0;
_updateIndex(int index) {
widget.onTabSelected(index);
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
List<Widget> items = List.generate(widget.items.length, (int index) {
return _buildTabItem(
item: widget.items[index],
index: index,
onPressed: _updateIndex,
);
});
items.insert(items.length >> 1, _buildMiddleTabItem());
return BottomAppBar(
shape: widget.notchedShape,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: items,
),
color: widget.backgroundColor,
);
}
Widget _buildMiddleTabItem() {
return Expanded(
child: SizedBox(
height: widget.height,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: widget.iconSize),
Text(
widget.centerItemText ?? '',
style: TextStyle(color: widget.color),
),
],
),
),
);
}
Widget _buildTabItem({
FABBottomAppBarItem item,
int index,
ValueChanged<int> onPressed,
}) {
Color color = _selectedIndex == index ? widget.selectedColor : widget.color;
return Expanded(
child: SizedBox(
height: widget.height,
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => onPressed(index),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(item.iconData, color: color, size: widget.iconSize),
Text(
item.text,
style: TextStyle(color: color),
)
],
),
),
),
),
);
}
}
And I implemented that custom like this:
bottomNavigationBar: FABBottomAppBar(
centerItemText: 'เสา',
color: Colors.grey,
backgroundColor: Colors.white,
selectedColor: Colors.red,
notchedShape: CircularNotchedRectangle(),
onTabSelected: _onTapped,
items: [
FABBottomAppBarItem(iconData: Icons.home, text: 'หน้าแรก'),
FABBottomAppBarItem(iconData: Icons.search, text: 'ค้นหา'),
FABBottomAppBarItem(iconData: Icons.account_circle, text: 'โปรไฟล์'),
FABBottomAppBarItem(iconData: Icons.more_horiz, text: 'อื่นๆ'),
],
),
body: _list[_page],
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
onPressed: () {
},
child: Icon(Icons.add),
elevation: 2.0,
),
),
And the result of that code is:
How to make my Custom Nav Bar like the design? Because in the design, the FAB/Asset/Icon inside the Bottom Navigation Bar and have curved upwards as I marked with arrows in the design.
I edited CircularNotchedRectangle.
Use CircularOuterNotchedRectangle instead of CircularNotchedRectangle.
PS. I added extraOffset param for extra thick. but it's not working exactly correct. But I just wanted to show you how to approach.
class CircularOuterNotchedRectangle extends NotchedShape {
/// Creates a [CircularOuterNotchedRectangle].
///
/// The same object can be used to create multiple shapes.
const CircularOuterNotchedRectangle({this.extraOffset = 10.0});
final double extraOffset;
/// Creates a [Path] that describes a rectangle with a smooth circular notch.
///
/// `host` is the bounding box for the returned shape. Conceptually this is
/// the rectangle to which the notch will be applied.
///
/// `guest` is the bounding box of a circle that the notch accommodates. All
/// points in the circle bounded by `guest` will be outside of the returned
/// path.
///
/// The notch is curve that smoothly connects the host's top edge and
/// the guest circle.
// TODO(amirh): add an example diagram here.
#override
Path getOuterPath(Rect host, Rect guest) {
if (guest == null || !host.overlaps(guest)) return Path()..addRect(host);
// The guest's shape is a circle bounded by the guest rectangle.
// So the guest's radius is half the guest width.
final double notchRadius = guest.width / 2.0;
// We build a path for the notch from 3 segments:
// Segment A - a Bezier curve from the host's top edge to segment B.
// Segment B - an arc with radius notchRadius.
// Segment C - a Bezier curve from segment B back to the host's top edge.
//
// A detailed explanation and the derivation of the formulas below is
const double s1 = 15.0;
const double s2 = 1.0;
final double r = notchRadius + extraOffset/2;
final double a = -1.0 * r - s2;
final double b = host.top + guest.center.dy;
final double n2 = math.sqrt(b * b * r * r * (a * a + b * b - r * r));
final double p2xA = ((a * r * r) - n2) / (a * a + b * b);
final double p2xB = ((a * r * r) + n2) / (a * a + b * b);
final double p2yA = math.sqrt(r * r - p2xA * p2xA) - extraOffset/2;
final double p2yB = math.sqrt(r * r - p2xB * p2xB) - extraOffset/2;
final List<Offset> p = List<Offset>(6);
// p0, p1, and p2 are the control points for segment A.
p[0] = Offset(a - s1, b);
p[1] = Offset(a, b);
p[2] = p2yA > p2yB ? Offset(p2xA, -p2yA) : Offset(p2xB, p2yB);
// p3, p4, and p5 are the control points for segment B, which is a mirror
// of segment A around the y axis.
p[3] = Offset(-1.0 * p[2].dx, -p[2].dy);
p[4] = Offset(-1.0 * p[1].dx, p[1].dy);
p[5] = Offset(-1.0 * p[0].dx, p[0].dy);
// translate all points back to the absolute coordinate system.
for (int i = 0; i < p.length; i += 1) p[i] += guest.center;
return Path()
..moveTo(host.left, -host.top)
..lineTo(p[0].dx, p[0].dy)
..quadraticBezierTo(p[1].dx, p[1].dy, p[2].dx, -p[2].dy)
..arcToPoint(
p[3],
radius: Radius.circular(notchRadius),
clockwise: true,
)
..quadraticBezierTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy)
..lineTo(host.right, host.top)
..lineTo(host.right, host.bottom)
..lineTo(host.left, host.bottom)
..close();
}
}
Please try this one, its may helps you
Scaffold(
backgroundColor: Colors.blueAccent,
floatingActionButton: Padding(
padding: EdgeInsets.only(top: 20),
child: SizedBox(
height: 70,
width: 70,
child: FloatingActionButton(
backgroundColor: Colors.transparent,
elevation: 0,
onPressed: () {},
child: Container(
height: 75,
width: 75,
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 4),
shape: BoxShape.circle,
color: Colors.red
),
child: Icon(Icons.add, size: 40),
),
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: new Container(
height: 80.0,
color: Colors.white,
padding: new EdgeInsets.only(top: 20.0),
child: new Theme(
data: Theme.of(context).copyWith(
// sets the background color of the `BottomNavigationBar`
canvasColor: Colors.white,
// sets the active color of the `BottomNavigationBar` if `Brightness` is light
primaryColor: Colors.red,
bottomAppBarColor: Colors.green,
textTheme: Theme
.of(context)
.textTheme
.copyWith(caption: new TextStyle(color: Colors.grey))), // sets the inactive color of the `BottomNavigationBar`
child:
new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex:0 ,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: new Text('Home'),
backgroundColor: Colors.black
),
BottomNavigationBarItem(
icon: new Icon(Icons.search),
title: new Text('Search'),
),
BottomNavigationBarItem(
icon: Icon(Icons.bookmark_border,color: Colors.transparent,),
title: Text('Center')
),
BottomNavigationBarItem(
icon: Icon(Icons.perm_identity),
title: Text('Person')
),
BottomNavigationBarItem(
icon: Icon(Icons.more_horiz),
title: Text('More')
),
]),
),
),
)
I would probably wrap your FloatingActionButton in a ClipRRect and Container like
floatingActionButton: ClipRRect(
borderRadius: BorderRadius.circular(80),
child: Container(
color: Colors.black,
padding: EdgeInsets.all(8),
child: FloatingActionButton(
onPressed: () {},
),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
Giving as color same of the BottomNavigationBoar. It should work

Flutter layout issue: stack z-order not working

The z-order of stack children layout is not as my expectation.
I have 4 widgets in a stack with keys: 0,1,2,3. I try to change their z-order and rebuild the stack widget. Here is my code:
class TestBringToFrontPage extends StatefulWidget {
TestBringToFrontPage({Key key}) : super(key: key);
#override
State<TestBringToFrontPage> createState() => TestBringToFrontState();
}
class TestBringToFrontState extends State<TestBringToFrontPage> {
List<Widget> _widgets = [];
#override
Widget build(BuildContext context) {
return Stack(children: <Widget>[
..._widgets,
Positioned(
bottom: 0,
right: 0,
child: RaisedButton(
child: Text(
"click",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
_swap(0, 2);
_swap(1, 3);
print("$_widgets");
});
},
)),
]);
}
#override
void initState() {
super.initState();
const double start = 100;
const double size = 100;
_widgets = <Widget>[
_buildWidget(key: const Key("0"), color: Colors.blue, offset: const Offset(start, start), size: const Size(size, size)),
_buildWidget(key: const Key("1"), color: Colors.green, offset: const Offset(start + size / 2, start), size: const Size(size, size)),
_buildWidget(key: const Key("2"), color: Colors.yellow, offset: const Offset(start, start + size / 2), size: const Size(size, size)),
_buildWidget(key: const Key("3"), color: Colors.red, offset: const Offset(start + size / 2, start + size / 2), size: const Size(size, size)),
];
}
Widget _buildWidget({Key key, Color color, Offset offset, Size size}) {
final label = (key as ValueKey<String>)?.value;
return Positioned(
key: key,
left: 0,
top: 0,
child: Container(
transform: Matrix4.identity()..translate(offset.dx, offset.dy, 0),
width: size.width,
height: size.height,
decoration: BoxDecoration(border: Border.all(color: Colors.black, width: 1.0), color: color),
child: Text(
label,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w700,
decoration: TextDecoration.none,
fontSize: 15.0,
),
),
));
}
void _swap(int x, int y) {
final w = _widgets[x];
_widgets[x] = _widgets[y];
_widgets[y] = w;
}
}
At starting, stack children are laid out in z-order (from bottom to top): 0,1,2,3. Screen shows:
Starting screen state image
By clicking to the button, I expect the stack children are laid out in new z-order: 2,3,0,1. And screen should show:
Expected screen state image
But not as expected, screen shows:
Actual screen state image.
Console log when clicking the button: "[Positioned-[<'2'>](left: 0.0, top: 0.0), Positioned-[<'3'>](left: 0.0, top: 0.0), Positioned-[<'0'>](left: 0.0, top: 0.0), Positioned-[<'1'>](left: 0.0, top: 0.0)]"
In Flutter inspectors window, the stack children are in correct order: 2,3,0,1. But stack renders wrong.
Do you know where am I wrong or is it flutter layout issue?
Thank you in advance,
After playing around with your code for a little while, I've managed to figure out that the Key on each block is holding some state that messes their ordering up during setState. Removing the keys gives me the behaviour you're expecting. I don't know why this would be happening, but at least now you have a fix if the keys aren't needed for whatever your goal is.
Whether they are or not, I would file a bug report if I were you, because I'm pretty sure this behaviour isn't intended.
Here's the code I used that works (I cleaned up a little bit of it so it doesn't override initState):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: TestBringToFrontPage(),
);
}
}
class TestBringToFrontPage extends StatefulWidget {
TestBringToFrontPage({Key key}) : super(key: key);
#override
State<TestBringToFrontPage> createState() => TestBringToFrontState();
}
class TestBringToFrontState extends State<TestBringToFrontPage> {
static const start = 50.0;
static const size = 250.0;
final _widgets = <Widget>[
_buildWidget(label: "0", color: Colors.blue, offset: const Offset(start, start), size: const Size(size, size)),
_buildWidget(label: "1", color: Colors.green, offset: const Offset(start + size / 2, start), size: const Size(size, size)),
_buildWidget(label: "2", color: Colors.yellow, offset: const Offset(start, start + size / 2), size: const Size(size, size)),
_buildWidget(label: "3", color: Colors.red, offset: const Offset(start + size / 2, start + size / 2), size: const Size(size, size)),
];
#override
Widget build(BuildContext context) {
return Stack(children: <Widget>[
..._widgets,
Positioned(
bottom: 0,
right: 0,
child: RaisedButton(
child: Text(
"click",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
_swap(0, 2);
_swap(1, 3);
print("$_widgets");
});
},
)),
]);
}
static Widget _buildWidget({String label, Color color, Offset offset, Size size}) {
return Positioned(
left: 0,
top: 0,
child: Container(
transform: Matrix4.identity()..translate(offset.dx, offset.dy, 0),
width: size.width,
height: size.height,
decoration: BoxDecoration(border: Border.all(color: Colors.black, width: 1.0), color: color),
child: Text(
label,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w700,
decoration: TextDecoration.none,
fontSize: 30.0,
),
),
));
}
void _swap(int x, int y) {
final w = _widgets[x];
_widgets[x] = _widgets[y];
_widgets[y] = w;
}
}

How to make the Bottom Navigation Bar overlay the main page in Flutter?

I'm building an app that has a Bottom Navigation Bar and I want to provide to this bar the ability to overlay the main page when I change its height in order to prevent the contents in the main page to be reduced to fit the available area. How can I do that?
I think that explaining it by images could be better, so there we go:
This is how the main page looks like together with the Bottom Navigation Bar:
Normal App:
When I click in the Yellow Circle Button (middle button), the BottomAppBar has to increase its height, but when I do this, the main content reduces its size to fit the available space.
Weird App:
I want to prevent it. How can I do it?
This is my code:
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _appBarHeight = 60.0;
void _openBottomAppBar() {
setState(() {
if (_appBarHeight > 60) {
_appBarHeight = 60.0;
} else {
_appBarHeight = 300.0;
}
});
}
#override
Widget build(BuildContext context) {
final Shader textGradient = LinearGradient(
colors: <Color>[Theme.of(context).primaryColorDark, Theme.of(context).primaryColorLight],
).createShader(Rect.fromLTWH(0.0, 0.0, 200.0, 70.0));
return Scaffold(
body: Container(
child: Column(
children: <Widget>[
Expanded(
flex: 3,
child: ClipShadowPath(
clipper: ApplicationBar(),
shadow: Shadow(blurRadius: 10),
child: LayoutBuilder(builder: (context, constraints) {
return Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage(
'images/tiny-squares.png',
),
repeat: ImageRepeat.repeat,
),
),
),
Container(
decoration: BoxDecoration(
backgroundBlendMode: BlendMode.multiply,
gradient: LinearGradient(
begin: const Alignment(-1.0, 0.0),
end: const Alignment(0.6, 0.0),
colors: <Color>[Theme.of(context).primaryColorDark, Theme.of(context).primaryColorLight],
),
),
),
Positioned(
right: constraints.biggest.width * 0.1,
top: constraints.biggest.height * 0.35,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
),
child: Material(
elevation: 4.0,
clipBehavior: Clip.antiAliasWithSaveLayer,
type: MaterialType.circle,
color: Colors.transparent,
)),
Padding(
padding: EdgeInsets.only(top: 8),
),
],
))
],
);
})),
),
Expanded(
flex: 4,
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.only(top: 16, left: 64),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 8),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
),
Padding(
padding: EdgeInsets.only(top: 8),
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
),
)
],
),
),
],
),
))
],
)),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
onPressed: _openBottomAppBar,
tooltip: 'Increment',
child: Text('A'),
elevation: 10.0,
),
bottomNavigationBar: NotchedBottomBar(
centerItemText: '',
height: _appBarHeight,
color: Colors.grey,
selectedColor: Theme.of(context).accentColor,
notchedShape: FollowerNotchedShape(inverted: true),
items: [
NotchedBottomBarItem(iconData: Icons.home, text: 'This'),
NotchedBottomBarItem(iconData: Icons.payment, text: 'is'),
NotchedBottomBarItem(iconData: Icons.input, text: 'Bottom'),
NotchedBottomBarItem(iconData: Icons.settings_applications, text: 'Bar'),
],
),
);
}
}
class ApplicationBar extends CustomClipper<Path> {
#override
Path getClip(Size size) {
final path = Path();
path.lineTo(size.width, 0);
path.lineTo(size.width, 0.84 * size.height);
path.lineTo(0.72 * size.width, 0.983 * size.height);
final firstEndPoint = new Offset(0.7 * size.width, (0.986 + 0.0065) * size.height);
final controlPoint = new Offset(0.675 * size.width, 0.989 * size.height);
path.quadraticBezierTo(firstEndPoint.dx, firstEndPoint.dy, controlPoint.dx, controlPoint.dy);
path.lineTo(0, 0.87 * size.height);
path.close();
return path;
}
#override
bool shouldReclip(YEXApplicationBar oldClipper) {
return oldClipper != this;
}
}
Code for the Bottom Bar
import 'package:flutter/material.dart';
import 'dart:math' as math;
class FollowerNotchedShape extends CircularNotchedRectangle {
int _inverterMultiplier;
#override
Path getOuterPath(Rect host, Rect guest) {
if (!host.overlaps(guest)) return Path()..addRect(host);
final double notchRadius = guest.width / 2.0;
const double s1 = 15.0;
const double s2 = 1.0;
final double r = notchRadius;
final double a = -1.0 * r - s2;
final double b = host.top - guest.center.dy;
final double n2 = math.sqrt(b * b * r * r * (a * a + b * b - r * r));
final double p2xA = ((a * r * r) - n2) / (a * a + b * b);
final double p2xB = ((a * r * r) + n2) / (a * a + b * b);
final double p2yA = math.sqrt(r * r - p2xA * p2xA);
final double p2yB = math.sqrt(r * r - p2xB * p2xB);
final List<Offset> p = List<Offset>(6);
// p0, p1, and p2 are the control points for segment A.
p[0] = Offset(a - s1, b);
p[1] = Offset(a, b);
final double cmp = b < 0 ? -1.0 : 1.0;
p[2] =
cmp * p2yA > cmp * p2yB ? Offset(p2xA, _inverterMultiplier * p2yA) : Offset(p2xB, _inverterMultiplier * p2yB);
// p3, p4, and p5 are the control points for segment B, which is a mirror
// of segment A around the y axis.
p[3] = Offset(-1.0 * p[2].dx, p[2].dy);
p[4] = Offset(-1.0 * p[1].dx, p[1].dy);
p[5] = Offset(-1.0 * p[0].dx, p[0].dy);
// translate all points back to the absolute coordinate system.
for (int i = 0; i < p.length; i += 1) p[i] += guest.center;
final Path path = Path()
..moveTo(host.left, -host.top)
..lineTo(p[0].dx, -p[0].dy)
..quadraticBezierTo(p[1].dx, p[1].dy, p[2].dx, p[2].dy);
if (guest.height == guest.width) {
// circle
path.arcToPoint(
p[3],
radius: Radius.circular(notchRadius),
clockwise: _inverterMultiplier == 1 ? false : true,
);
} else {
// stadium
path
..arcToPoint(
(_inverterMultiplier == 1 ? guest.bottomLeft : guest.topLeft) + Offset(guest.height / 2, 0), // here
radius: Radius.circular(guest.height / 2),
clockwise: _inverterMultiplier == 1 ? false : true,
)
..lineTo(guest.right - guest.height / 2, (_inverterMultiplier == 1 ? guest.bottom : guest.top)) // here
..arcToPoint(
p[3],
radius: Radius.circular(guest.height / 2),
clockwise: _inverterMultiplier == 1 ? false : true,
);
}
path
..quadraticBezierTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy)
..lineTo(host.right, host.top)
..lineTo(host.right, host.bottom)
..lineTo(host.left, host.bottom)
..close();
return path;
}
FollowerNotchedShape({inverted: false}) {
if (inverted) {
_inverterMultiplier = -1;
} else
_inverterMultiplier = 1;
}
}
class NotchedBottomBarItem {
NotchedBottomBarItem({this.iconData, this.text});
IconData iconData;
String text;
}
class NotchedBottomBar extends StatefulWidget {
NotchedBottomBar({
this.items,
this.centerItemText,
this.height: 60.0,
this.iconSize: 24.0,
this.backgroundColor,
this.color,
this.selectedColor,
this.notchedShape,
this.onTabSelected,
}) {
assert(this.items.length == 2 || this.items.length == 4);
}
final List<NotchedBottomBarItem> items;
final String centerItemText;
double height;
final double iconSize;
final Color backgroundColor;
final Color color;
final Color selectedColor;
final NotchedShape notchedShape;
final ValueChanged<int> onTabSelected;
#override
State<StatefulWidget> createState() => NotchedBottomBarState();
}
class NotchedBottomBarState extends State<NotchedBottomBar> {
int _selectedIndex = 0;
_updateIndex(int index) {
widget.onTabSelected(index);
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
List<Widget> items = List.generate(widget.items.length, (int index) {
return _buildTabItem(
item: widget.items[index],
index: index,
onPressed: _updateIndex,
);
});
items.insert(items.length >> 1, _buildMiddleTabItem());
return BottomAppBar(
shape: widget.notchedShape,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: items,
),
color: widget.backgroundColor,
);
}
Widget _buildMiddleTabItem() {
return Expanded(
child: SizedBox(
height: widget.height,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: widget.iconSize),
Text(
widget.centerItemText ?? '',
style: TextStyle(color: widget.color),
),
],
),
),
);
}
Widget _buildTabItem({
NotchedBottomBarItem item,
int index,
ValueChanged<int> onPressed,
}) {
Color color = _selectedIndex == index ? widget.selectedColor : widget.color;
return Expanded(
child: SizedBox(
height: widget.height,
child: Material(
type: MaterialType.transparency,
color: Colors.transparent,
child: InkWell(
onTap: () {
onPressed(index);
},
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 8),
),
Icon(item.iconData, color: color, size: widget.iconSize),
Padding(
padding: EdgeInsets.only(bottom: 4),
),
Text(
item.text,
style: TextStyle(color: color),
)
],
),
),
),
),
);
}
}
Any help?
use "extendBody: true" inside "Scaffold" class
Scaffold(
extendBody: true,
body: Container()