I am working on a project with textfields that are members of a reorderable list. My problem is when the list is accessed from a Navigator push to a new screen the text fields within the ReorderableListView on the new screen glitches causing the keyboard to pop up then quickly disappear. The code is identical for both lists, does anyone know what may be causing this bug? I created a sample main.dart to demonstrate the effect:
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Reorderable List bug',
home: MainPage(),
);
}
}
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ReorderableListView(
onReorder: (old_index, new_index) {},
children: <Widget>[
textCard(),
textCard2()
],
),
),
Text('Keyboard behaves as expected here but will glitch on following page'),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
child: Text('Go to SubPage'),
onPressed: () {
navigateToSubPage(context);
},
)
],
),
),
);
}
Future navigateToSubPage(context) async {
Navigator.push(context, MaterialPageRoute(builder: (context) => SubPage()));
}
}
class SubPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ReorderableListView(
onReorder: (old_index, new_index) {},
children: <Widget>[
textCard(),
textCard2(),
RaisedButton(
key: ValueKey('3'),
textColor: Colors.white,
color: Colors.redAccent,
child: Text('Back to Main Page'),
onPressed: () {
Navigator.pop(context);
},
)
],
),
);
}
}
Widget textCard() {
return Card(
key: ValueKey('1'),
child: Padding(
padding: EdgeInsets.all(15.0),
child: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 15.0),
child: Text(
'1.',
)),
Expanded(
child: TextFormField(
decoration: InputDecoration(labelText: 'Enter text'),
)
)
],
)));
}
Widget textCard2() {
return Card(
key: ValueKey('2'),
child: TextFormField(
decoration: InputDecoration(
labelText: 'Enter text'
),
),
);
}
Related
The form are inside CustomScrollView and I want alertbar always pinned below appbar and disappear when tab X.
Currently code
import 'package:flutter/material.dart';
class BaseAppBar extends StatelessWidget {
final Widget title;
final bool innerBoxIsScrolled;
BaseAppBar({this.title, this.innerBoxIsScrolled=false});
#override
Widget build(BuildContext context) {
return SliverAppBar(
backgroundColor: Colors.amber,
pinned: true,
floating: false,
forceElevated: innerBoxIsScrolled,
title: title,
leading: FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
child: Icon(
Icons.arrow_back_ios,
color: Colors.white,
size: 20,
),
onPressed: () {
Navigator.of(context).pop();
},
)
);
}
}
class BaseLayout extends StatelessWidget {
final Widget appBar;
final Widget alertBar;
final Widget child;
BaseLayout({this.appBar, this.alertBar, this.child});
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
BaseAppBar(
title: Text(
'test'
),
),
SliverToBoxAdapter(
child: alertBar,
),
SliverToBoxAdapter(
child: child,
)
],
);
}
}
I think it is better to do it this way. Don't overcomplicate things with useless Widget inheritance for the AlertBar.
class _BaseLayoutState extends State<BaseLayout> {
bool _showAlert = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
height: MediaQuery.of(context).size.height,
child: CustomScrollView(
slivers: <Widget>[
BaseAppBar(
title: Text('test'),
),
_showAlert
? SliverToBoxAdapter(
child: SizedBox(
height: 80.0,
child: ListTile(
leading: Icon(Icons.error_outline),
title: Text("Please correct form data."),
trailing: IconButton(
onPressed: () {
_showAlert = false;
setState(() {});
},
icon: Icon(Icons.clear),
),
),
),
)
: SliverToBoxAdapter(
child: SizedBox(),
),
/// The rest of the screen where the form and text fields are
SliverFillRemaining(
child: ListView(
children: <Widget>[
Form(
child: Column(
children: <Widget>[
TextFormField(),
TextFormField(),
TextFormField()
],
),
),
/// alert button
Center(
child: RaisedButton(
child: Text('ALERT!'),
onPressed: () {
_showAlert = true;
/// make it go away after a few seconds
Future.delayed(Duration(seconds: 3), () {
_showAlert = false;
setState(() {});
});
setState(() {});
},
),
),
],
),
),
],
),
),
);
}
}
I want to make a bottom navigation bar, with a Floating Action Button, in Flutter
when I click the Floating Action Button, the window will appear on the screen,
the photo in below will help to understand my request:
You can copy paste run full code below
You can use OverlayEntry and set color
entry = OverlayEntry(
builder: (context) {
return Center(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.black.withOpacity(0.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
onPressed: () {
entry.remove();
},
child: Text("abc"),
working demo
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class BottomAppBarPage extends StatefulWidget {
#override
_BottomAppBarPageState createState() => _BottomAppBarPageState();
}
class _BottomAppBarPageState extends State<BottomAppBarPage> {
OverlayEntry entry;
#override
void initState() {
super.initState();
entry = OverlayEntry(
builder: (context) {
return Center(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.black.withOpacity(0.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
onPressed: () {
entry.remove();
},
child: Text("abc"),
),
RaisedButton(
onPressed: () {
entry.remove();
},
child: Text("def"),
),
RaisedButton(
onPressed: () {
entry.remove();
},
child: Text("123"),
),
],
),
),
);
},
);
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Bottom App Bar')),
body: ListView(
padding: const EdgeInsets.all(8),
children: <Widget>[
Container(
height: 50,
color: Colors.amber[600],
child: const Center(child: Text('Entry A')),
),
Container(
height: 50,
color: Colors.amber[500],
child: const Center(child: Text('Entry B')),
),
Container(
height: 50,
color: Colors.amber[100],
child: const Center(child: Text('Entry C')),
),
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
Overlay.of(context).insert(entry);
},
),
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
notchMargin: 4.0,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
)
],
),
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: BottomAppBarPage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I am trying to create a form builder, In one screen I want have small container with icon and text with gesture detector, when I tap on the container it should contruct a new DatePicker Widget. I know how to do it in the same screen, but onTap I want construct the Datepicker in another screen.
This is Form Component Class.
class FormComponents extends StatefulWidget {
#override
_FormComponentsState createState() => _FormComponentsState();
}
class _FormComponentsState extends State<FormComponents> {
final GlobalKey<FormBuilderState> _fbKey = GlobalKey<FormBuilderState>();
int _count = 0;
final formData = FormWidgetData(widget: DatePicker());
#override
Widget build(BuildContext context) {
List<Widget> datePicker = List.generate(_count, (index) => DatePicker());
return Scaffold(
appBar: AppBar(
title: Text('Form Components'),
),
body: Container(
margin: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
FormBuilder(
key: _fbKey,
initialValue: {
'date': DateTime.now(),
'accept_terms': false,
},
autovalidate: true,
child: Column(
children: <Widget>[
Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_count = _count + 1;
});
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Date Picker'),
duration: Duration(seconds: 2),
),
);
},
child: Container(
margin: EdgeInsets.all(16.0),
padding: EdgeInsets.all(8.0),
height: 100.0,
width: 120.0,
color: Colors.black,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Icon(
Icons.date_range,
color: Colors.white,
),
Text(
'Date Picker',
style: TextStyle(
color: Colors.white, fontSize: 18.0),
),
],
),
),
);
},
),
Container(
height: 200.0,
margin: EdgeInsets.all(8.0),
child: Expanded(
child: ListView(
children: datePicker,
),
),
),
],
),
),
],
),
),
);
}
}
This is my Date Picker Class
class DatePicker extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: FormBuilderDateTimePicker(
attribute: 'date',
inputType: InputType.date,
format: DateFormat('yyyy-MM-dd'),
decoration: InputDecoration(labelText: 'AppointmentTime'),
),
);
}
}
This is my Form Builder Class, How to do I Construct the above Date Picker class here.
class MyFormBuilder extends StatefulWidget {
final FormWidgetData data;
MyFormBuilder({this.data});
#override
_MyFormBuilderState createState() => _MyFormBuilderState();
}
class _MyFormBuilderState extends State<MyFormBuilder> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Form Builder'),
),
body: Container(
height: 300.0,
margin: EdgeInsets.all(8.0),
child: Expanded(
child: ListView(
children: <Widget>[],
),
),
),
);
}
}
SolidBottomSheetController() is not working in my code, I am not able to listen_events of height or anything, hopefully, and I am sure my code is correct.
Can Anyone Please Give Example of SolidBottomSheet() working, with Controller, how you are implementing and listening to the events
You can copy paste run full code below , it's from official example and add listen event
You can use _controller.heightStream.listen
code snippet
SolidController _controller = SolidController();
#override
void initState() {
_controller.heightStream.listen((event) {
print(event);
});
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:solid_bottom_sheet/solid_bottom_sheet.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
SolidController _controller = SolidController();
#override
void initState() {
_controller.heightStream.listen((event) {
print(event);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Solid bottom sheet example"),
),
body: ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
Image.asset("assets/cardImg.png"),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Flutter rules?",
style: Theme.of(context).textTheme.title,
),
),
],
),
ButtonTheme.bar(
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('NOPE'),
onPressed: () {
/* ... */
},
),
FlatButton(
child: const Text('YEAH'),
onPressed: () {
/* ... */
},
),
],
),
),
],
),
);
},
),
bottomSheet: SolidBottomSheet(
controller: _controller,
draggableBody: true,
headerBar: Container(
color: Theme.of(context).primaryColor,
height: 50,
child: Center(
child: Text("Swipe me!"),
),
),
body: Container(
color: Colors.white,
height: 30,
child: Center(
child: Text(
"Hello! I'm a bottom sheet ",
style: Theme.of(context).textTheme.display1,
),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.stars),
onPressed: () {
_controller.isOpened ? _controller.hide() : _controller.show();
}),
);
}
}
At the homescreen of myApp() I have a stateless widget, it contains a MaterialApp and a Scaffold. Scaffold have a property of drawer and I passed I created a drawer, and one of the item in my drawer needs to open the showModalBottomSheet while closing the drawer. How can I achieve this? I've tried passing the context itself, and as globalKey.currentContext (after GlobalKey<ScaffoldState> globalKey = GlobalKey();) but the drawer sometimes closes, other time gives me a NoMethodFoundException (or something like that)
In short, how to have a Scaffold drawer that have one of the item, when tapped closes the drawer and showModalBottomSheet?
Current code:
class Timeline extends StatelessWidget {
#override
Widget build(BuildContext context) {
GlobalKey<ScaffoldState> homeScaffoldKey = GlobalKey();
return MaterialApp(
title: "Test",
theme: ThemeData(
appBarTheme: AppBarTheme(iconTheme: IconThemeData(color: Colors.black)),
),
home: Scaffold(
key: homeScaffoldKey,
drawer: showDrawer(homeScaffoldKey.currentContext),
backgroundColor: Colors.grey[100],
body: Stack(
children: <Widget>[
HomePageView(),
AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
),
],
),
),
);
}
}
Drawer showDrawer(BuildContext context) {
void showCalendarsModalBottom() {
showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return ListView.builder(
itemCount: repo.calendars.length,
itemBuilder: (builder, index) {
return StatefulBuilder(
builder: (builder, StateSetter setState) => ListTile(
leading: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Checkbox(
value: repo.getIsEnabledCal(repo.getCal(index)),
onChanged: (value) {
setState(() {
repo.toggleCalendar(repo.getCal(index));
});
},
),
Container(
height: 14,
width: 14,
margin: EdgeInsets.only(left: 2, right: 6),
decoration: BoxDecoration(
color: Colors.redAccent,
shape: BoxShape.circle,
),
),
Text(
repo.getCal(index).name,
style: TextStyle(
fontSize: 16,
),
),
],
),
onTap: () {
setState(() {
repo.toggleCalendar(repo.getCal(index));
});
},
),
);
},
);
},
);
}
return Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Align(
child: Text('Timeline', textScaleFactor: 2),
alignment: Alignment.bottomLeft,
),
),
ListTile(
title: Text('Dark Mode'),
onTap: () => Navigator.pop(context),
),
ListTile(
title: Text('Calenders'),
onTap: () {
Navigator.pop(context);
showCalendarsModalBottom();
},
)
],
),
);
}
Updated working code based on your code snippet:
You'll need to have statefulwidget that will help to pass the context from drawer to bottomsheet and pass the context as an argument in showCalendarModalBottomSheet() method.
void main() {
runApp(new MaterialApp(home: Timeline(), debugShowCheckedModeBanner: false));
}
class Timeline extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Test",
theme: ThemeData(
appBarTheme: AppBarTheme(iconTheme: IconThemeData(color: Colors.black)),
),
home: MyHomePage()
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: AppDrawer(),
backgroundColor: Colors.grey[100],
body: Stack(
children: <Widget>[
//HomePageView(),
AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
)
],
)
);
}
Widget AppDrawer() {
return Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Align(
child: Text('Timeline', textScaleFactor: 2),
alignment: Alignment.bottomLeft,
),
),
ListTile(
title: Text('Dark Mode'),
onTap: () => Navigator.pop(context),
),
ListTile(
title: Text('Calenders'),
onTap: () {
Navigator.of(context).pop();
showCalendarsModalBottom(context);
},
)
],
),
);
}
Future<Null> showCalendarsModalBottom(context) {
return showModalBottomSheet(context: context, builder: (context) => Container(
color: Colors.red,
// your code here
));
}
}
And the output is: When app drawer menu Calendar is tapped, it closes and opens the bottomsheet seamlessly. If you tap on app drawer again and repeat steps, you see smooth transition between drawer and bottomsheet. Hope this answers your question.