Skip to content

Instantly share code, notes, and snippets.

@rydmike
Last active August 16, 2020 20:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rydmike/4fb5f2d1316115913fc41a26463486e6 to your computer and use it in GitHub Desktop.
Save rydmike/4fb5f2d1316115913fc41a26463486e6 to your computer and use it in GitHub Desktop.
Flutter: Floating action button location is wrong for top locations when using Scaffold property 'extendBodyBehindAppBar'
// MIT License
// Copyright 2020 Mike Rydstrom
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(FabIssueDemo());
}
class FabIssueDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
systemNavigationBarColor: Colors.grey[100],
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
systemNavigationBarIconBrightness: Brightness.dark,
),
);
return MaterialApp(
title: 'FAB location issue',
theme: ThemeData(
primarySwatch: Colors.indigo,
scaffoldBackgroundColor: Colors.grey[100],
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: Colors.red[800],
),
buttonTheme: ButtonThemeData(
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.indigo),
textTheme: ButtonTextTheme.primary,
),
),
debugShowCheckedModeBanner: false,
home: IssuePage(),
);
}
}
class IssuePage extends StatefulWidget {
static const Map<FloatingActionButtonLocation, Widget> fabLocations =
<FloatingActionButtonLocation, Widget>{
FloatingActionButtonLocation.startTop: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'Start top',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
FloatingActionButtonLocation.centerTop: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'Center top',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
FloatingActionButtonLocation.endTop: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'End top',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
FloatingActionButtonLocation.startFloat: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'Start float',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
FloatingActionButtonLocation.centerFloat: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'Center float',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
FloatingActionButtonLocation.endFloat: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'End float',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
FloatingActionButtonLocation.startDocked: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'Start docked',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
FloatingActionButtonLocation.centerDocked: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'Center docked',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
FloatingActionButtonLocation.endDocked: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'End docked',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
),
};
@override
State<StatefulWidget> createState() {
return _IssuePageState();
}
}
class _IssuePageState extends State<IssuePage> {
bool behindAppBar;
bool behindBody;
bool showAppBar;
bool showBottomBar;
FloatingActionButtonLocation fabLocation;
int selectedIndex;
@override
void initState() {
behindAppBar = false;
behindBody = true;
showAppBar = true;
showBottomBar = true;
fabLocation = FloatingActionButtonLocation.endTop;
selectedIndex = 0;
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: behindAppBar,
extendBody: behindBody,
floatingActionButtonLocation: fabLocation,
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
appBar: showAppBar
? AppBar(
title: const Text('FAB location issue'),
centerTitle: true,
elevation: 0,
backgroundColor: Colors.transparent,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.topRight,
colors: <Color>[
Colors.indigo,
Colors.indigo.withOpacity(0.85),
],
),
),
child: null,
),
)
: null,
bottomNavigationBar: showBottomBar
? BottomNavigationBar(
backgroundColor: Theme.of(context).canvasColor.withOpacity(0.85),
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('Business'),
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
title: Text('School'),
),
],
currentIndex: selectedIndex,
onTap: (int value) {
setState(() {
selectedIndex = value;
});
},
)
: null,
body: ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16),
child: Text(
'FAB location issue demo',
style: Theme.of(context).textTheme.headline5,
),
),
const Divider(),
const ListTile(
title: Text('Floating action button location issue'),
subtitle: Text(
"When using property 'extendBodyBehindAppBar' the FAB gets "
"wrong position when using 'FloatingActionButtonLocation' "
'with top positions. Same issue does not happen with any bottom '
"location when using the 'extendBody', it works correctly. ",
),
),
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: CupertinoSegmentedControl<FloatingActionButtonLocation>(
children: IssuePage.fabLocations,
groupValue: fabLocation,
onValueChanged: (FloatingActionButtonLocation value) {
setState(() {
fabLocation = value;
});
},
borderColor: Theme.of(context).brightness == Brightness.dark
? Theme.of(context).primaryColorLight
: Theme.of(context).colorScheme.primary,
selectedColor: Theme.of(context).brightness == Brightness.dark
? Theme.of(context).primaryColorLight
: Theme.of(context).colorScheme.primary,
unselectedColor: Theme.of(context).cardColor,
),
),
const Divider(),
SwitchListTile(
title: const Text('Extend body behind app bar'),
subtitle:
const Text('Top FAB locations FAIL when you extend body behind '
'the app bar. Location should remain the same as when body '
'is not extended behind the app bar.'),
value: behindAppBar,
onChanged: (bool value) {
setState(() {
behindAppBar = value;
});
},
),
const Divider(),
SwitchListTile(
title: const Text('Extend body behind bottom bar'),
subtitle:
const Text('Bottom FAB locations work OK also when you extend '
'content behind it'),
value: behindBody,
onChanged: (bool value) {
setState(() {
behindBody = value;
});
},
),
const Divider(),
SwitchListTile(
title: const Text('Show app bar'),
subtitle:
const Text('To double check the location you can also try this '
'without an app bar'),
value: showAppBar,
onChanged: (bool value) {
setState(() {
showAppBar = value;
});
},
),
const Divider(),
SwitchListTile(
title: const Text('Show bottom navigation bar'),
subtitle:
const Text('To double check the location you can also try this '
'without a bottom navigation bar'),
value: showBottomBar,
onChanged: (bool value) {
setState(() {
showBottomBar = value;
});
},
),
const Divider(),
for (int i = 1; i <= 50; i++)
Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
color: Colors.primaries[i % Colors.primaries.length][600],
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
'CARD $i\n'
'A few cards so we can scroll behind app bar and bottom '
'navigation bar and see when body content extends behind '
'the app bar and bottom bar that has some opacity.',
style: Theme.of(context).primaryTextTheme.subtitle1),
)),
],
),
);
}
}
@rydmike
Copy link
Author

rydmike commented Aug 16, 2020

Sample code for Flutter issue: flutter/flutter#63915

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment