I'm creating a flutter app that requires a user to complete certain tasks in order to stop an alarm clock. It's a more immersive alarm clock app that could help stimulate the brain in order to aid in the process of waking up.
The tasks are simple questions the user has to answer. I'm utilizing TextFeild() to allow the user to input their answer. However, when I go to debug my code is redirected to a file called box.dart this is where the error is stated.
Here is the box.dart file error:
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Cannot hit test a render box with no size.'),
describeForError('The hitTest() method was called on this RenderBox'),
ErrorDescription(
'Although this node is not marked as needing layout, '
'its size is not set.',
),
ErrorHint(
'A RenderBox object must have an '
'explicit size before it can be hit-tested. Make sure '
'that the RenderBox in question sets its size during layout.',
),
]);
}
Here is my full code:
import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart';
//import 'package:audioplayers/audio_cache.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Time Attack',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text('Time Attack'),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
// ignore: avoid_print
print("alarm is playing");
final player = AudioCache();
//bool isPLaying = false;
await player.load('musicForapp.mp3');
AudioPlayer p = AudioPlayer();
p.audioCache = player;
await p.play(AssetSource('musicForapp.mp3'));
},
backgroundColor: Colors.red,
child: const Icon(Icons.punch_clock),
),
body: Padding(
padding: const EdgeInsets.all(170),
child: Column(
children: [
const Text(
"Click the Icon in the bottem right to simulate alarm",
style: TextStyle(
color: Colors.white,
fontSize: 30.0,
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const TasksPage(),
),
);
}, //on pressed
child: const Text(
"Tasks page",
style: TextStyle(color: Colors.white),
),
)
],
),
),
),
);
} //widget build
}
class TasksPage extends StatefulWidget {
const TasksPage({super.key});
#override
State<TasksPage> createState() => _TasksPageState();
}
class _TasksPageState extends State<TasksPage> {
final _textcontroller = TextEditingController();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text('Tasks'),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const SecondTask(),
),
);
},
backgroundColor: Colors.red,
child: const Text("Next"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
"How many states are there in the United States of America?",
style: TextStyle(
backgroundColor: Colors.white,
color: Colors.black,
fontSize: 30.0,
),
),
Expanded(
child: TextField(
controller: _textcontroller,
decoration: InputDecoration(
hintText: "Type your answer here!",
border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {
_textcontroller.clear();
},
icon: const Icon(Icons.clear),
),
),
),
),
],
),
),
),
),
);
} //widget build
}
class SecondTask extends StatefulWidget {
const SecondTask({super.key});
#override
State<SecondTask> createState() => _SecondTask();
}
class _SecondTask extends State<SecondTask> {
final _textcontroller2 = TextEditingController();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text('Tasks'),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const Thirdtask(),
),
);
},
backgroundColor: Colors.red,
child: const Text("Next"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
"how many oceans are there?",
style: TextStyle(
backgroundColor: Colors.white,
color: Colors.black,
fontSize: 30.0,
),
),
Expanded(
child: TextField(
controller: _textcontroller2,
decoration: InputDecoration(
hintText: "Type your answer here!",
border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {
_textcontroller2.clear();
},
icon: const Icon(Icons.clear),
),
),
),
),
],
),
),
),
),
);
} //widget build
}
class Thirdtask extends StatefulWidget {
const Thirdtask({super.key});
#override
State<Thirdtask> createState() => _Thirdtask();
}
class _Thirdtask extends State<Thirdtask> {
final _textcontroller3 = TextEditingController();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text('Tasks'),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const Finaltask(),
),
);
},
backgroundColor: Colors.red,
child: const Text("Next"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
"What is 8 x 7?",
style: TextStyle(
backgroundColor: Colors.white,
color: Colors.black,
fontSize: 30.0,
),
),
Expanded(
child: TextField(
controller: _textcontroller3,
decoration: InputDecoration(
hintText: "Type your answer here!",
border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {
_textcontroller3.clear();
},
icon: const Icon(Icons.clear),
),
),
),
),
],
),
),
),
),
);
} //widget build
}
class Finaltask extends StatefulWidget {
const Finaltask({super.key});
#override
State<Finaltask> createState() => _Finaltask();
}
class _Finaltask extends State<Finaltask> {
final _textcontroller4 = TextEditingController();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text('Tasks'),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const Congrats(),
),
);
},
backgroundColor: Colors.red,
child: const Text("Submit"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text(
"Riddle: What goes up but never comes back down?",
style: TextStyle(
backgroundColor: Colors.white,
color: Colors.black,
fontSize: 30.0,
),
),
Expanded(
child: TextField(
controller: _textcontroller4,
decoration: InputDecoration(
hintText: "Type your answer here!",
border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {
_textcontroller4.clear();
},
icon: const Icon(Icons.clear),
),
),
),
),
],
),
),
),
),
);
} //widget build
}
class Congrats extends StatefulWidget {
const Congrats({super.key});
#override
State<Congrats> createState() => _Congrats();
}
class _Congrats extends State<Congrats> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text('Good Job!'),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const MyHomePage(),
),
);
//player.clear('explosion.mp3'); (to stop alarm)
// ignore: avoid_print
print("Alarm has stoped");
},
backgroundColor: Colors.red,
child: const Text("Home"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: const [
Expanded(
child: Text(
"Congratulations you completed your tasks, Click the home button to return home and stop the timer!",
style: TextStyle(
backgroundColor: Colors.white,
color: Colors.black,
fontSize: 30.0,
),
),
),
],
),
),
),
),
);
} //widget build
}
Firstly, please delete all MaterialApp (except the first on the top). Because in Flutter app there must be only one MaterialApp throughout the code.
Secondly, juste change your padding 170 to 17 for exemple. 170 is extremely big.
Use single MaterialApp on top widget as #Antonin Liehn said. Now for the TextFormField border. You need to use enabledBorder, also try others border property , Find more about OutlineInputBorder.
decoration: InputDecoration(
hintText: "Type your answer here!",
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 22,
),
),
errorBorder: ,
focusedBorder: ,
disabledBorder: ,
focusedErrorBorder: ,
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 22,
),
),
Also wrap text with Flexible/Expanded widget. You cal follow this widget
class TasksPage extends StatefulWidget {
const TasksPage({super.key});
#override
State<TasksPage> createState() => _TasksPageState();
}
class _TasksPageState extends State<TasksPage> {
final _textcontroller = TextEditingController();
final border = const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
width: 22,
));
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.red,
title: const Text('Taskssssssss'),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const SecondTask(),
),
);
},
backgroundColor: Colors.red,
child: const Text("Next"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Flexible(
child: const Text(
"How many states are there in the United States of America?",
style: TextStyle(
backgroundColor: Colors.white,
color: Colors.black,
fontSize: 30.0,
),
),
),
Expanded(
child: TextFormField(
controller: _textcontroller,
decoration: InputDecoration(
hintText: "Type your answer here!",
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white,
),
),
errorBorder: border,
focusedBorder: border,
disabledBorder: border,
focusedErrorBorder: border,
border: border,
suffixIcon: IconButton(
onPressed: () {
_textcontroller.clear();
},
icon: const Icon(Icons.clear),
),
),
),
),
],
),
),
),
);
} //widget build
}
Related
main.dart
import 'package:flutter/material.dart';
import 'package:stray_animal_emergencyrescue/signUpPage.dart';
import './commons/commonWidgets.dart';
import 'package:stray_animal_emergencyrescue/loggedIn.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 login UI',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
//String showPasswordText = "Show Password";
bool obscurePasswordText = true;
#override
Widget build(BuildContext context) {
final passwordField = TextField(
obscureText: obscurePasswordText,
decoration: InputDecoration(
//contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
hintText: "Password",
//border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
suffixIcon: IconButton(
icon: new Icon(Icons.remove_red_eye),
onPressed: () {
setState(() {
this.obscurePasswordText = !obscurePasswordText;
});
},
)),
);
final loginButon = Material(
//elevation: 5.0,
//borderRadius: BorderRadius.circular(30.0),
color: Colors.blue,
child: MaterialButton(
//minWidth: MediaQuery.of(context).size.width,
//padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
//print(MediaQuery.of(context).size.width);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LogIn()),
);
},
child: Text('Login', textAlign: TextAlign.center),
),
);
final facebookContinueButton = Material(
//borderRadius: BorderRadius.circular(30.0),
color: Colors.blue,
child: MaterialButton(
//minWidth: MediaQuery.of(context).size.width,
//padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
//print(MediaQuery.of(context).size.width);
},
child: Text('Facebook', textAlign: TextAlign.center),
),
);
final googleContinueButton = Material(
//borderRadius: BorderRadius.circular(30.0),
color: Colors.blue,
child: MaterialButton(
//minWidth: MediaQuery.of(context).size.width,
//padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
//print(MediaQuery.of(context).size.width);
},
child: Text('Google ', textAlign: TextAlign.center),
),
);
final signUpButton = Material(
//borderRadius: BorderRadius.circular(30.0),
color: Colors.blue,
child: MaterialButton(
//minWidth: MediaQuery.of(context).size.width,
//padding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => FormScreen()),
);
//print(MediaQuery.of(context).size.width);
},
child: Text('Sign Up ', textAlign: TextAlign.center),
),
);
return Scaffold(
appBar: AppBar(
title: Text("Animal Emergency App"),
),
body: Center(
child: Container(
//color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(36.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//SizedBox(height: 45.0),
getTextFieldWidget(),
SizedBox(height: 15.0),
passwordField,
sizedBoxWidget,
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
facebookContinueButton,
SizedBox(width: 5),
googleContinueButton,
SizedBox(width: 5),
loginButon
],
),
/*loginButon,
signUpButton,*/
sizedBoxWidget,
const Divider(
color: Colors.black,
height: 20,
thickness: 1,
indent: 20,
endIndent: 0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
//crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
signUpButton
],
),
],
),
),
),
),
);
}
}
loggedIn.dart
import 'package:flutter/material.dart';
import './tabbarviews/emergencyresue/EmergencyHome.dart';
import './tabbarviews/animalcruelty/animalCrueltyHome.dart';
import './tabbarviews/bloodbank/bloodBankHome.dart';
class LogIn extends StatefulWidget {
#override
_LogInState createState() => _LogInState();
}
class _LogInState extends State<LogIn> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static List<Widget> _widgetOptions = <Widget>[
EmergencyHome(),
AnimalCrueltyHome(),
BloodBankHome()
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First app bar appearing'),
actions: <Widget>[
GestureDetector(
onTap: () {},
child: CircleAvatar(
//child: Text("SC"),
backgroundImage: AssetImage('assets/images/760279.jpg'),
//backgroundImage: ,
),
),
IconButton(
icon: Icon(Icons.more_vert),
color: Colors.white,
onPressed: () {},
),
],
),
drawer: Drawer(
child: ListView(
children: <Widget>[
new ListTile(title: Text("Primary")),
MyListTile(
"Home",
false,
"Your customized News Feed about people you follow, ongoing rescues, nearby activities, adoptions etc.",
3,
Icons.home,
true,
() {}),
MyListTile(
"News & Media Coverage",
false,
"News about incidents which need immediate action, changing Laws",
3,
Icons.home,
false,
() {}),
MyListTile(
"Report",
true,
"Report cases with evidences anonymously",
3,
Icons.announcement,
false,
() {}),
MyListTile(
"Blood Bank",
true,
"Details to donate blood ",
3,
Icons.medical_services,
false,
() {}),
],
),
),
body: _widgetOptions[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
selectedItemColor: Colors.blue,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.pets),
label: 'Emergency Rescue',
),
BottomNavigationBarItem(
icon: Icon(Icons.add_alert),
label: 'Report Cruelty',
),
BottomNavigationBarItem(
icon: Icon(Icons.medical_services),
label: 'Blood Bank',
),
/*BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'Safe Hands',
backgroundColor: Colors.blue),*/
],
onTap: _onItemTapped,
),
);
}
}
//Safe Hands
class MyListTile extends StatelessWidget {
final String title;
final bool isThreeLine;
final String subtitle;
final int maxLines;
final IconData icon;
final bool selected;
final Function onTap;
MyListTile(this.title, this.isThreeLine, this.subtitle, this.maxLines,
this.icon, this.selected, this.onTap);
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(title),
isThreeLine: isThreeLine,
subtitle:
Text(subtitle, maxLines: maxLines, style: TextStyle(fontSize: 12)),
leading: Icon(icon),
selected: selected,
onTap: onTap);
}
}
EmergencyHome.dart
import 'package:flutter/material.dart';
import './finishedAnimalEmergencies.dart';
import './reportAnimalEmergency.dart';
import './ongoingAnimalEmergencies.dart';
class EmergencyHome extends StatefulWidget {
#override
_EmergencyHomeState createState() => _EmergencyHomeState();
}
class _EmergencyHomeState extends State<EmergencyHome> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text("Second appBar appearing"),
bottom: TabBar(
tabs: [
Tab(
//icon: Icon(Icons.more_vert),
text: "Report",
),
Tab(
text: "Ongoing",
),
Tab(
text: "Finished",
)
],
),
),
body: TabBarView(
children: [
ReportAnimalEmergency(),
OngoingAnimalEmergencies(),
FinishedAnimalEmergencies(),
],
),
)
);
}
}
The issue I am facing is two appBar, I tried removing appBar from loggedIn.dart but Drawer hamburger icon is not showing, and I cannot remove appBar from emergencyHome.dart as I wont be able to add Tab bar. What is viable solution for this? Please help how to Structure by app and routes to easily manage navigation within app
Remove the appbar from EmergencyHome.dart
this will remove the second app title. But there will be that shadow from the first app bar so put elvation:0
so, this will look like one appbar now your drawer will also work.
you can use flexibleSpace in EmergencyHome.dart
DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
flexibleSpace: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
TabBar(
tabs: [
Tab(
//icon: Icon(Icons.more_vert),
text: "Report",
),
Tab(
text: "Ongoing",
),
Tab(
text: "Finished",
)
],
)
],
),
),
body: TabBarView(
children: [
ReportAnimalEmergency(),
OngoingAnimalEmergencies(),
FinishedAnimalEmergencies(),
],
),
)
);
You don't want to make two appbar to get the drawer property. Use DefaultTabController then inside that you can use scaffold.so, you can have drawer: Drawer() inside that you can also get a appbar with it with TabBar as it's bottom.
This is most suitable for you according to your use case.
i will put the full code below so you can copy it.
void main() {
runApp(const TabBarDemo());
}
class TabBarDemo extends StatelessWidget {
const TabBarDemo({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
drawer: Drawer(),
appBar: AppBar(
bottom: const TabBar(
tabs: [
Tab(
text: "report",
),
Tab(text: "ongoing"),
Tab(text: "completed"),
],
),
title: const Text('Tabs Demo'),
),
body: const TabBarView(
children: [
Icon(Icons.directions_car),
Icon(Icons.directions_transit),
Icon(Icons.directions_bike),
],
),
),
),
);
}
}
OUTPUT
I want to put a custom toolbar on the keypad like the image above. Is it possible in flutter? or should I write code on the iOS or Android side?
You can copy paste run full code below
Please see working demo below
You can use package https://pub.dev/packages/keyboard_overlay
Step 1: Use with HandleFocusNodesOverlayMixin
Step 2: Use FocusNodeOverlay for focusNode
Step 3: Use GetFocusNodeOverlay and set _focusNodeOverlay = GetFocusNodeOverlay(
Step 4: TextField use TextField(focusNode: _focusNodeOverlay,
code snippet
class _MyHomePageState extends State<MyHomePage>
with HandleFocusNodesOverlayMixin {
FocusNodeOverlay _focusNodeOverlay;
#override
void initState() {
_focusNodeOverlay = GetFocusNodeOverlay(
child: TopKeyboardUtil(
Container(
color: Colors.white,
height: 45,
width: MediaQueryData.fromWindow(ui.window).size.width,
child: Row(
children: [
GestureDetector(
child: Icon(Icons.save),
onTap: () => print("click"),
),
...
Spacer(),
Container(
width: 60,
child: Center(
child: DoneButtonIos(
backgroundColor: Colors.white,
textColor: Colors.green,
label: 'Post',
onSubmitted: () {
print("submit");
},
platforms: ['android', 'ios'],
),
),
),
],
),
),
),
);
working demo
full code
import 'package:flutter/material.dart';
import 'package:keyboard_overlay/keyboard_overlay.dart';
import 'dart:ui' as ui;
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(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with HandleFocusNodesOverlayMixin {
FocusNodeOverlay _focusNodeOverlay;
#override
void initState() {
_focusNodeOverlay = GetFocusNodeOverlay(
child: TopKeyboardUtil(
Container(
color: Colors.white,
height: 45,
width: MediaQueryData.fromWindow(ui.window).size.width,
child: Row(
children: [
GestureDetector(
child: Icon(Icons.save),
onTap: () => print("click"),
),
GestureDetector(
child: Icon(Icons.computer),
onTap: () => print("click"),
),
GestureDetector(
child: Icon(Icons.home),
onTap: () => print("click"),
),
Spacer(),
Container(
width: 60,
child: Center(
child: DoneButtonIos(
backgroundColor: Colors.white,
textColor: Colors.green,
label: 'Post',
onSubmitted: () {
print("submit");
},
platforms: ['android', 'ios'],
),
),
),
],
),
),
),
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
focusNode: _focusNodeOverlay,
style: TextStyle(color: Colors.grey),
decoration: InputDecoration(
labelText: 'Type Something',
labelStyle: TextStyle(color: Colors.black),
fillColor: Colors.orange,
hintStyle: TextStyle(
color: Colors.grey,
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.black, width: 1.0),
),
),
),
],
),
),
);
}
}
Yes there is a way around in the flutter to achieve this.
Create a widget of the toolbar you want to add.
Set it visible on input focus.
For reference I am sharing the code how I achieve that.
class InputDoneView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Style.lightGrey,
child: Align(
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.only(top: 1.0, bottom: 1.0),
child: CupertinoButton(
padding: EdgeInsets.only(right: 24.0, top: 2.0, bottom: 2.0),
onPressed: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: Text(
"Done",
style: TextStyle(color: Style.primaryColor,fontWeight: FontWeight.normal)
),
),
),
),
);
}
}
To call this in your main view when input field is focused in and out.
showOverlay(BuildContext context) {
if (overlayEntry != null) return;
OverlayState overlayState = Overlay.of(context);
overlayEntry = OverlayEntry(builder: (context) {
return Positioned(
bottom: MediaQuery.of(context).viewInsets.bottom, right: 0.0, left: 0.0, child: InputDoneView());
});
overlayState.insert(overlayEntry);
}
removeOverlay() {
if (overlayEntry != null) {
overlayEntry.remove();
overlayEntry = null;
}
}
Following Problem, I'd like to have Space between the AppBar and the MealFormField.
I tried to Wrap the MealFormField into a Container or Column and then Add some Space, but this is not working.
I Added a Picture of the Screen and draw where the Space between the AppBar and the MealFormField should be added.
Code
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
import 'package:intl/intl.dart';
import 'package:mealapp/bloc/auth/auth_bloc.dart';
import 'package:mealapp/bloc/form/add_meal_form_bloc.dart';
import 'package:mealapp/components/loading.dart';
import 'package:mealapp/models/global.dart';
import 'package:mealapp/services/repositories/meal_repository.dart';
import 'package:status_alert/status_alert.dart';
class MealFormScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => AddMealFormBloc(
mealRepository: MealRepository(),
authBloc: context.bloc<AuthBloc>(),
),
child: Scaffold(
appBar: AppBar(
title: Text(
'Add Meal',
style: darkTodoTitle.copyWith(fontSize: 24),
),
centerTitle: true,
iconTheme: IconThemeData(color: Colors.black),
elevation: 0,
backgroundColor: Colors.white,
brightness: Brightness.light,
),
body: MealFormFields(),
),
);
}
}
class MealFormFields extends StatelessWidget {
#override
Widget build(BuildContext context) {
final formBloc = context.bloc<AddMealFormBloc>();
return FormBlocListener<AddMealFormBloc, String, String>(
onSuccess: (context, _) {
LoadingDialog.hide();
StatusAlert.show(
context,
duration: Duration(seconds: 2),
title: 'Success',
subtitle: 'Successfully added a meal.',
configuration: IconConfiguration(icon: Icons.done),
);
ExtendedNavigator.rootNavigator.pop();
},
onSubmitting: (context, _) {
LoadingDialog.show(context);
},
child: Padding(
padding: const EdgeInsets.all(24.0),
child: ListView(
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Add your new meal",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 24),
TextFieldBlocBuilder(
textFieldBloc: formBloc.mealNameField,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red),
),
labelText: 'Enter meal name',
),
),
DateTimeFieldBlocBuilder(
dateTimeFieldBloc: formBloc.mealDateField,
format: DateFormat("dd-MM-yyyy"),
initialDate: DateTime.now(),
firstDate: DateTime.now().add(Duration(days: 0)),
lastDate: DateTime.now().add(Duration(days: 365)),
decoration: InputDecoration(
prefixIcon: Icon(Icons.calendar_today),
),
),
FlatButton(
onPressed: () {
print(formBloc.state.canSubmit);
formBloc.submit();
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Text("Save"),
color: redColor,
textColor: Colors.white,
),
],
),
),
);
}
}
You should wrap your MealFormFields(), with a Padding Widget like so:
class MealFormScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => AddMealFormBloc(
mealRepository: MealRepository(),
authBloc: context.bloc<AuthBloc>(),
),
child: Scaffold(
appBar: AppBar(
title: Text(
'Add Meal',
style: darkTodoTitle.copyWith(fontSize: 24),
),
centerTitle: true,
iconTheme: IconThemeData(color: Colors.black),
elevation: 0,
backgroundColor: Colors.white,
brightness: Brightness.light,
),
body: Padding(
// I set the padding here to only the top, you can increase the double (16.0) to suit exactly what you're looking to achieve visually
padding: EdgeInsets.only(top: 16.0),
child: MealFormFields(),
),
),
);
}
}
Hope it works for you.
I have a Scaffold screen (ListsScreen).
Which has a Button(AddNewListButton) that opens up a Modal Bottom Sheet (ListScreenBottomSheetWidget).
Bottom Sheet has TextField (ListTitleInputTextFieldWidget).
When i tap the TextField to open the keyboard, the parent screen rebuilds itself, due to which ofcourse all its child widgets are rebuilt as well.
Why is this happening? I was under the impression that state changes only rebuild themselves or their children, not their parents. And i also added const constructors almost everywhere to avoid rebuilds but this is still happening.
The Parent ListsScreen:
class ListsScreen extends StatelessWidget {
const ListsScreen();
static const routeName = '/lists-screen';
#override
Widget build(BuildContext context) {
final user = Provider.of<AuthProvider>(context, listen: false).getUser;
print('stateless rebuilding');
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Text(
'${user['name']}\'s Lists',
style: TextStyle(
color: Theme.of(context).primaryColorLight,
),
),
actions: <Widget>[
const SignOutButton(),
],
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
SizeConfig.smallDevice
? Text(
'Welcome to TODOS',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.grey[700],
),
)
: Text(
'Welcome to TODOS',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
color: Colors.grey[700],
),
),
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
const AddNewListButton(),
SizeConfig.smallDevice
? const SizedBox(
height: 30,
)
: const SizedBox(
height: 40,
),
const UserListsListViewWidget(),
],
),
),
),
);
}
}
class SignOutButton extends StatelessWidget {
const SignOutButton();
Future<void> _submitRequest(BuildContext context) async {
_showLoadingAlert(context);
try {
await Provider.of<AuthProvider>(context, listen: false)
.submitLogOutRequest();
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed(LoginScreen.routeName);
} on HttpExceptions catch (error) {
Navigator.of(context).pop();
_showErrorDialogue(error.getErrorList, context);
}
}
void _showErrorDialogue(List<dynamic> errorMessages, BuildContext context) {
showDialog(
context: context,
builder: (ctx) => Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: ErrorListWidget(errorMessages: errorMessages),
),
);
}
void _showLoadingAlert(BuildContext context) {
showDialog(
context: context,
builder: (ctx) => const LoadingWidget(),
);
}
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () => _submitRequest(context),
child: Row(
children: <Widget>[
Text(
'Sign Out',
style: TextStyle(
color: Theme.of(context).primaryColorLight,
),
),
Icon(
Icons.exit_to_app,
color: Theme.of(context).primaryColorLight,
),
],
),
);
}
}
class AddNewListButton extends StatelessWidget {
const AddNewListButton();
void _modalBottomSheetMenu(BuildContext context) {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
isScrollControlled: true,
builder: (builder) {
return const ListScreenBottomSheetWidget();
},
);
}
#override
Widget build(BuildContext context) {
return RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
elevation: 10,
color: Theme.of(context).primaryColor,
onPressed: () => _modalBottomSheetMenu(context),
child: Text(
'+ Add List',
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.smallDevice ? 10 : 15,
),
),
);
}
}
The Modal Bottom Sheet:
import 'package:flutter/material.dart';
import 'package:todo_spicotech/helpers/size_config.dart';
class ListScreenBottomSheetWidget extends StatelessWidget {
const ListScreenBottomSheetWidget();
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
currentFocus.unfocus();
},
child: Container(
margin: const EdgeInsets.all(20.0),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Material(
borderRadius: BorderRadius.all(Radius.circular(15)),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizeConfig.smallDevice
? Text(
'Create a new List',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
color: Colors.grey[700],
),
)
: Text(
'Create a new List',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
color: Colors.grey[700],
),
),
SizeConfig.smallDevice
? const SizedBox(
height: 20,
)
: const SizedBox(
height: 30,
),
const ListTitleInputTextFieldWidget(),
SizeConfig.smallDevice
? const SizedBox(
height: 20,
)
: const SizedBox(
height: 30,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
borderRadius: BorderRadius.circular(5),
onTap: () {
Navigator.of(context).pop();
},
child: Ink(
padding: EdgeInsets.all(10),
child: const Text('CANCEL'),
),
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
elevation: 10,
color: Theme.of(context).primaryColor,
onPressed: () {},
child: Text(
'Create',
style: TextStyle(
color: Colors.white,
fontSize: SizeConfig.smallDevice ? 10 : 15,
),
),
),
],
),
],
),
),
),
),
);
}
}
class ListTitleInputTextFieldWidget extends StatefulWidget {
const ListTitleInputTextFieldWidget();
#override
_ListTitleInputTextFieldWidgetState createState() => _ListTitleInputTextFieldWidgetState();
}
class _ListTitleInputTextFieldWidgetState extends State<ListTitleInputTextFieldWidget> {
#override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.lightBlue,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.lightBlue,
),
),
labelText: 'List Title',
contentPadding: EdgeInsets.all(10),
),
);
}
}
when you call showModalBottomSheet , it actually use Navigator inside
return Navigator.of(context, rootNavigator: useRootNavigator).push(_ModalBottomSheetRoute<T>(
builder: builder,
source code of showModalBottomSheet https://github.com/flutter/flutter/blob/17079f26b54c8517678699a0cefe5f7bfec67b3f/packages/flutter/lib/src/material/bottom_sheet.dart#L635
Flutter teams' reply of issue Pages on Navigator stack rebuild when a new page is pushed https://github.com/flutter/flutter/issues/11655#issuecomment-348287396
This is working as intended. In general, you should assume that all widgets can rebuild at any time, that they don't is mostly just an optimisation.
In particular, routes will rebuild because their navigator state has changed so they might need to update how they draw back buttons and the like.
I have noticed creating forms with flutter. That if there is Scaffold with a text field focused i.e the key pad visible if one presses the back button.
Navigator.pop();
This results in an render flex error.
I have tried wrapping the child in the single child scroll view components also tried setting the resize to avoid bottom padding property to true.
I still get the error.
Has anyone else experienced this ?
if so what is the solution and cause of the error ?
EDIT:
import 'package:flutter/material.dart';
class SettingsScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text(
"Settings",
style: TextStyle(
color: primaryColor,
fontFamily: titleFontStyle
),
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: primaryColor),
onPressed: () {
Navigator.of(context).pop();
},
),
centerTitle: true,
backgroundColor: Colors.white,
elevation: 0.0,
),
backgroundColor: Colors.white,
body: Container(
// width: MediaQuery.of(context).size.width - 10.0,
// height: MediaQuery.of(context).size.height,
// padding: EdgeInsets.fromLTRB(20.0, 60.0, 20.0, 0.0),
child: SettingsForm(),
),
);
}
}
class SettingsForm extends StatefulWidget {
#override
_SettingsFormState createState() => _SettingsFormState();
}
class _SettingsFormState extends State<SettingsForm> {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(top: 50.0),
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
TextField(
autofocus: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Username',
),
),
SizedBox(height: 8.0),
RaisedButton(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 40.0),
textColor: Colors.white,
color: primaryColor,
child: Text(
'Update',
style: TextStyle(
color: Colors.white
),
),
onPressed: () {},
),
],
),
),
);
}
}
I have tried that, and it works fine. The keyboard layout will close when you press back button and navigate you to previous page.
Here is my 2 dart files below:
main.dart (From which I navigating through)
import 'settings.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: new HomeScreen()
);
}
}
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Title"),
),
body: new Center(child: new Text("Click Mee")),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
backgroundColor: Colors.orange,
onPressed: () {
print("Clicked");
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new SettingsForm()),
);
},
),
);
}
}
settings.dart (your provided code on next screen)
class SettingsForm extends StatefulWidget {
SettingsForm();
#override
_SettingsFormState createState() => _SettingsFormState();
}
class _SettingsFormState extends State<SettingsForm> {
final _formKey = GlobalKey<FormState>();
Color primaryColor = const Color.fromRGBO(3, 155, 226, 1);
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text(
"Settings",
style: TextStyle(
color: primaryColor
),
),
leading: IconButton(
icon: Icon(Icons.arrow_back, color: primaryColor),
onPressed: () {
FocusScope.of(context).unfocus();
Navigator.of(context).pop();
print("yes");
},
),
centerTitle: true,
backgroundColor: Colors.white,
elevation: 0.0,
),
backgroundColor: Colors.white,
body: Container(
// width: MediaQuery.of(context).size.width - 10.0,
// height: MediaQuery.of(context).size.height,
// padding: EdgeInsets.fromLTRB(20.0, 60.0, 20.0, 0.0),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(top: 50.0),
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.0, right: 24.0),
children: <Widget>[
TextField(
autofocus: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Username',
),
),
SizedBox(height: 8.0),
RaisedButton(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 40.0),
textColor: Colors.white,
color: primaryColor,
child: Text(
'Update',
style: TextStyle(
color: Colors.white
),
),
onPressed: () {},
),
],
),
),
),
),
);
}
}
The issue was that when I navigate back to the previous component. It was the previous component that overflowed.
I had used Column in the said component.
Converting it into ListView solved the error.