Align Icon Vertically Centered in a Row: Flutter - flutter

I am developing a bottom navigation bar which hides upon scrolling up, but I'm unable to achieve the desired result. I have 2 containers with different widgets to handle the separately. In first containers, there are 2 text widgets with a slider and second container has 5 icon buttons. I wat to reduce the space between Texts and slider and align the play_button in vertical centerzz. What I'm trying to make is this:
What have I developed:
this is my code:
bottomNavigationBar: ValueListenableBuilder(
valueListenable: hiding.visible,
builder: (context, bool value, child) => AnimatedContainer(
color: light_mode? Color(0xFFFFFFFF) : Color(0xFF616161),
duration: Duration(milliseconds: 500),
height: value ? 100 : 0.0,
child: Wrap(
runAlignment: WrapAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(5, 1, 5, 1),
padding: EdgeInsets.all(5),
height: 30,
width: MediaQuery.of(context).size.width,
child: Row(
children: <Widget>[
Text("0:00", style: TextStyle(color: Colors.black), textAlign: TextAlign.left),
Container(
margin: EdgeInsets.all(2),
height: 30,
width: MediaQuery.of(context).size.width-80,
child:SliderTheme(
data: SliderThemeData(
thumbColor: light_mode? Color(0xFF6A0080) : Colors.black,
activeTrackColor: light_mode?Color(0xFF6A0080) : Colors.black,
inactiveTrackColor: Colors.grey,
trackHeight: 1.0,
),
child: Slider(
value: 60.0,
max: 100.0,
min: 0.0,
onChanged: (double newValue) {
setState(() {
// sliderValue = newValue.round();
});
},
))),
Text("0:00", style: TextStyle(color: Colors.black), textAlign: TextAlign.right)
],
),
),
Container(
alignment: Alignment.center,
margin: EdgeInsets.fromLTRB(20, 1, 20, 16),
padding: EdgeInsets.all(8.0),
height: 50,
width: MediaQuery.of(context).size.width,
child: Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: <Widget>[
IconButton(
alignment: Alignment.center,
icon: Icon(
Icons.skip_previous,
color: Colors.grey,
size: 30,
),
onPressed: () {
// do something
},
),
IconButton(
padding: EdgeInsets.all(8.0),
icon: Icon(
Icons.play_circle_fill_rounded,
color: light_mode? Color(0xFFEA80FC) : Color(0xFF6D6D6D) ,
size: 45,
),
onPressed: () {
// do something
},
)
,
IconButton(
icon: Icon(
Icons.skip_next,
color: Colors.grey,
size: 30,
),
onPressed: () {
// do something
},
)
,
IconButton(
icon: Icon(
Icons.bookmark_border_outlined,
color: Colors.grey,
size: 35.0,
),
onPressed: () {
// do something
},
)
,
IconButton(
icon: Icon(
Icons.share_rounded,
color: Colors.grey,
),
onPressed: () {
// do something
},
)
],
),
)
],
),
)),
How can i properly align icons in lesser space?

to reduce the space between text and slider you need to give a custom shape to it. So for that create a shape as show below:-
class CustomTrackShape extends RoundedRectSliderTrackShape {
Rect getPreferredRect({
#required RenderBox parentBox,
Offset offset = Offset.zero,
#required SliderThemeData sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
final double trackHeight = sliderTheme.trackHeight;
final double trackLeft = offset.dx;
final double trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
final double trackWidth = parentBox.size.width;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
}
and then in your slider theme:-
SliderTheme(
data: SliderThemeData(
thumbColor: light_mode? Color(0xFF6A0080) : Colors.black,
activeTrackColor: light_mode?Color(0xFF6A0080) : Colors.black,
inactiveTrackColor: Colors.grey,
trackHeight: 1.0,
trackShape: CustomTrackShape(),//call your custom shape over here
),
child: Slider(
value: 60.0,
max: 100.0,
min: 0.0,
onChanged: (double newValue) {
setState(() {
// sliderValue = newValue.round();
});
},
)),
and for proper alignment of container remove the padding from top and bottom of your container containing thos icons i.e. change padding from padding:EdgeInsets.all(8.0) to padding:EdgeInsets.symmetric(horizontal:8.0), becuase the size of the container is 50 and of icon is 45 and you are providing padding of 10 pixels(5 from top and 5 from bottom) this means container height should be 55. So either increase the height upto 60 or change padding code.

Related

How to navigate to next page in a page indicator

I have a function to swipe from screen to screen and show the progress in a indicator bar that I've created and a elevated button with the 'next page' option.
My question is how to navigate to the "next" page with an elevated button on the onTap function. Basically, same result than when the user swipes left.
This is what I've achieved so far:
class xxxx extends State<xxxx> {
final int _numPages = 3;
final PageController _pageController = PageController(initialPage: 0);
int _currentPage = 0;
List<Widget> _buildPageIndicator() {
var list = <Widget>[];
for (var i = 0; i < _numPages; i++) {
list.add(i == _currentPage ? _indicator(true) : _indicator(false));
}
return list;
}
Widget _indicator(bool isActive) {
return AnimatedContainer(
duration: Duration(milliseconds: 150),
margin: EdgeInsets.symmetric(horizontal: 8.0),
height: 8.0,
width: isActive ? 24.0 : 16.0,
decoration: BoxDecoration(
color: isActive ? TheBaseColors.lightRed : Colors.grey,
borderRadius: BorderRadius.all(Radius.circular(12)),
),
);
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
Color background = Color(0xFF001d5d);
return Scaffold(
[...................]
bottomSheet: _currentPage == _numPages - 1
? Container(
height: size.height * 0.10,
color: background,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(children: _buildPageIndicator()),
//CHANGE BUTTON
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: TheBaseColors.lightRed,
padding: EdgeInsets.symmetric(horizontal: 50, vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
onPressed: () {
pushNewScreen(
context,
screen: Routes.getWidgetForRoute(Routes.questions, context),
pageTransitionAnimation: PageTransitionAnimation.cupertino,
);
},
child: Text(
'Get started',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.normal,
),
),
),
],
),
)
: Container(
height: size.height * 0.10,
color: background,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(children: _buildPageIndicator()),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: TheBaseColors.lightRed,
padding: EdgeInsets.symmetric(horizontal: 50, vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
onPressed: () {
pushNewScreen(
context,
screen: Routes.getWidgetForRoute(Routes.questions, context),
pageTransitionAnimation: PageTransitionAnimation.cupertino,
);
},
child: Text(
'Next',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.normal,
),
),
),
],
),
),
);
}
}
For that you can use these two functions i have created with the pagecontroller:
void nextPage(){
_pageController.animateToPage(_pageController.page.toInt() + 1,
duration: Duration(milliseconds: 400),
curve: Curves.easeIn
);
}
void previousPage(){
_pageController.animateToPage(_pageController.page.toInt() -1,
duration: Duration(milliseconds: 400),
curve: Curves.easeIn
);
}
you can change these according to you requirements.

Flutter gradient bottom line in ios (Home indicator in ios)

I need to remove or change color of that gradient bottom line and remove margin of bottom nav bar. There is no any problem with displaying layout in android. How to fix it? Here is my code:
Widget _buildContent(BuildContext context) {
return WillPopScope(
onWillPop: () async {
bool handle = !await widget._screenStates[_pressedPosition].currentState
.maybePop();
if (handle) {
return await _showExitDialog(context);
} else {
return handle;
}
},
child: SafeArea(
child: Stack(
fit: StackFit.expand,
children: <Widget>[
PageView(
physics: new NeverScrollableScrollPhysics(),
controller: _pageController,
children: _mainScreens(),
),
Align(
alignment: Alignment.bottomCenter,
child: CurvedNavigationBar(
animationDuration: Duration(milliseconds: 200),
backgroundColor: Colors.transparent,
onTap: (int position) {
_bottomTapped(position);
},
height: BottomAppBarHeight,
items: <Widget>[
ImageIcon(
AssetImage('assets/images/logo_small.png'),
color: PrimaryColor,
size: 40.0,
),
Icon(
Icons.list,
size: 25.0,
color: (_pressedPosition == 1)
? PrimaryColor
: DarkerGreyColor,
),
Icon(
Icons.shopping_cart,
size: 25.0,
color: (_pressedPosition == 2)
? PrimaryColor
: DarkerGreyColor,
),
Icon(
Icons.favorite,
size: 25.0,
color: (_pressedPosition == 3)
? PrimaryColor
: DarkerGreyColor,
),
Icon(
Icons.account_circle,
size: 25.0,
color: (_pressedPosition == 4)
? PrimaryColor
: DarkerGreyColor,
),
],
),
)
],
),
),
);
}
Thanks for your help! In addition, here is image with bottom gradient line:
I think that problem is in the safe area.
try fit:StackFit.loose,or
SafeArea(
bottom: false,
child: myWidgetThatFillsTheScreen,
)

I want to have 2 rows in a column where first row has only 1 large element due to which lot of space is wasted in flutter

UI of my flutter app
I want to shift insert and search button upward which are in a row but it is constrained by above row which contains only one big element(the circular slider) due to which the bounding box of whole row has increased. So, lot of space is wasted. What can be the workaround for this, I want to have variable size widgets in a row but the row bounding box should be fitted to individual widgets rather than taking the size of the biggest element.
Row(
children: [
Container(
padding: EdgeInsets.all(10),
child: Text('Available : '),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black38,
borderRadius: BorderRadius.circular(10)),
),
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
taxi.available = (taxi.available == 1) ? 0 : 1;
});
},
child: Availabilitybutton(taxi.available)),
),
Container(
padding: EdgeInsets.all(10),
child: Text('Condition : '),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black38,
borderRadius: BorderRadius.circular(10)),
),
SleekCircularSlider(
min: 0,
max: 10,
initialValue: 5,
appearance: CircularSliderAppearance(
size: 100,
startAngle: 180,
angleRange: 180,
customColors: CustomSliderColors(
trackColor: Colors.black38,
progressBarColors: [
Colors.green,
Colors.yellowAccent,
Colors.redAccent
],
dynamicGradient: false)),
innerWidget: (value) {
return Center(
child: Text(
'${value.round()}',
style: TextStyle(color: Colors.white, fontSize: 50),
),
);
},
onChange: (double value) {
taxi.carCondition = value.round();
print(value);
})
],
),
Row(
children: [
FlatButton(
onPressed: () {
setState(() {
choice = 0;
_save();
});
updateExpenseView();
},
child: Container(
padding: EdgeInsets.all(5),
color: Colors.black38,
child: Text(
'Insert',
style: TextStyle(color: Colors.white),
))),
FlatButton(
onPressed: () {
setState(() {
choice = 1;
_search(id: d_id);
});
},
child: Container(
padding: EdgeInsets.all(5),
color: Colors.black38,
child: Text(
'Search',
style: TextStyle(color: Colors.white),
)))
],
),
Rows will accommodate the biggest widget specified.
Instead how about you split the row containing the slider into 2 columns and the left column could further be split into 2 rows. The bottom row here can be used to put the insert and search widgets.
I was able to reduce row size by clipping the bigger circular slider element, since I had displayed only a semi-circle but the widget gives space for the whole circle.
ClipRect(
child: Align(
alignment: Alignment.topCenter,
heightFactor: 0.8,
child: SleekCircularSlider(
min: 0,
max: 10,
initialValue: 5,
appearance: CircularSliderAppearance(
size: 100,
startAngle: 180,
angleRange: 180,
customColors: CustomSliderColors(
trackColor: Colors.black38,
progressBarColors: [
Colors.green,
Colors.yellowAccent,
Colors.redAccent
],
dynamicGradient: false)),
innerWidget: (value) {
return Center(
child: Text(
'${value.round()}',
style:
TextStyle(color: Colors.white, fontSize: 50),
),
);
},
onChange: (double value) {
taxi.carCondition = value.round();
}),
),
)

How to prevent keyboard moving up an other widget in a stack? (flutter)

I've got a new transaction screen with several textfields and a container at the bottom for shortcuts:
And when i trigger a textfield the grey container moves up as well:
Here is my code for the screen:
//import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
//import 'package:flutter/services.dart';
import '../items/icon_picket_item.dart';
import '../items/select_transaction_item.dart';
import '../providers/user_transactions.dart';
import '../providers/icon_auswahl.dart';
import '../providers/account_type.dart';
import '../providers/user_settings/single_multiple_acc.dart';
import 'package:provider/provider.dart';
import '../storag/locale.dart';
import '../storag/foundation.dart';
import '../my_icons.dart';
class NewTransactionScreen extends StatefulWidget {
static const routeName = '/new-transaction';
#override
_NewTransactionScreenState createState() => _NewTransactionScreenState();
}
class _NewTransactionScreenState extends State<NewTransactionScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final _titleController = TextEditingController(text: '');
final _amountController = TextEditingController(text: '-10.00');
final _accountController = TextEditingController();
final _notesController = TextEditingController();
final _repeatController = TextEditingController();
DateTime _selectedDate;
Color _correct = Colors.white54;
final data = [];
void _colorCorrect() {
final enteredTitle = _titleController.text;
final enteredAmount = _amountController.text;
final enteredAccount = _accountController.text;
final enteredRepeat = _repeatController.text;
Color color;
if (enteredTitle.isEmpty ||
enteredAmount.isEmpty ||
enteredAccount.isEmpty ||
enteredRepeat.isEmpty) {
color = Colors.white54;
} else {
color = Colors.white;
}
setState(() {
_correct = color;
});
}
void _submitData(String choiceId, NewTransactions transactions, iconData,
GeldKonto kontoss) {
//onSubmitted gives you a string
if (_amountController.text.isEmpty) {
return;
}
final enteredTitle = _titleController.text;
final enteredAmount = _amountController.text;
final enteredAccount = _accountController.text;
final enteredNotes = _notesController.text;
final enteredRepeat = _repeatController.text;
Icon enteredIcon = iconData.taken;
if (enteredTitle.isEmpty ||
enteredAmount.isEmpty ||
enteredAccount.isEmpty ||
enteredRepeat.isEmpty) {
return; //means code stops here and addTx doesnt run
} else {
transactions.addNewtransaction(
xTitle: enteredTitle,
xAmount: double.parse(enteredAmount) <= 0
? double.parse(enteredAmount) * (-1)
: double.parse(enteredAmount),
xchosenDate: _selectedDate == null ? DateTime.now() : _selectedDate,
xAccountType: enteredAccount,
xNotes: enteredNotes,
xRepeat: enteredRepeat,
xIcon: enteredIcon,
xId: DateTime.now().toString(),
xchoiceId: choiceId);
}
Navigator.of(context).pop(context);
// .pop closes modalsheet , you have the property contexyt cause of extends state
}
Container _buildTransactionRow(
{Text typedText,
Function onPress,
comparisonStuff,
Icon icon,
double iconSpace,
double iconAfterSpace}) {
final mediaQuery = MediaQuery.of(context);
return Container(
// padding: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SizedBox(height: 1, width: iconSpace),
icon,
SizedBox(height: 1, width: iconAfterSpace), //25
SizedBox(
width: mediaQuery.size.width * 0.75,
child: GestureDetector(
onTap: onPress,
child: Column(
//crossAxisAlignment: CrossAxisAlignment.,
children: <Widget>[
SizedBox(height: 17, width: 1),
SizedBox(
width: mediaQuery.size.width * 0.75,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 1,
width: 4,
),
typedText
],
),
),
SizedBox(height: 10, width: mediaQuery.size.width * 0.04),
SizedBox(
height: 1,
width: mediaQuery.size.width * 0.75,
child: const Divider(
color: Colors.white54,
thickness: 1,
),
),
],
),
),
),
],
),
);
}
Widget _buildAppBar(String pageTitle, NewTransactions transactions,
GeldKonto kontoss, String choiceId) {
return AppBar(
automaticallyImplyLeading: false,
leading: isIos == true
? IconButton(
icon: Icon(CupertinoIcons.back, size: 30, color: Colors.white),
onPressed: () => Navigator.of(context).pop(context),
)
: IconButton(
icon: Icon(Icons.arrow_back, size: 30, color: Colors.white),
onPressed: () => Navigator.of(context).pop(context),
),
centerTitle: true,
title: Text(
pageTitle,
style: const TextStyle(
fontSize: 25,
color: Colors.white,
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.end,
),
actions: <Widget>[
Consumer<IconAuswahl>(
builder: (ctx, iconData, child) => IconButton(
padding: EdgeInsetsDirectional.only(
start: 15,
end: 25,
top: 5,
bottom: 0,
),
icon: Icon(
MyIcon.correct,
size: 45,
color: _correct,
),
onPressed: () =>
_submitData(choiceId, transactions, iconData, kontoss),
),
),
],
backgroundColor: Color(0xffb00374a),
);
}
Widget _buildColumn(
BuildContext context, AppBar appBar, local, String choiceId) {
final mediaQuery = MediaQuery.of(context);
return Stack(children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [Colors.black, Color(0xffb00374a)],
),
),
),
Positioned(
bottom: 0,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.08,
color: Colors.grey),
),
SingleChildScrollView(
child: Container(
height: (mediaQuery.size.height -
appBar.preferredSize.height -
mediaQuery.padding.top ) * 1.05 ,
padding: EdgeInsets.only(
top: 10,
left: 20,
right: 10,
bottom:0,
//
// so that the whole thing always move +10
),
child: Consumer<SinlgeOrMultiple>(
builder: (_, data, __) => data.multipleAcc == true
? Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
// CupertinoTextField(
// ),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? 'Bankkonto'
: 'Mein Ausgewähltes Zeug',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.account, size: 38),
iconSpace: 3,
iconAfterSpace: 20,
onPress: () {}),
Row(children: <Widget>[
SizedBox(width: 3.5),
choiceId == '0'
? const Icon(
MyIcon.paying,
color: Colors.white,
size: 25,
)
: const Icon(
MyIcon.earning,
color: Colors.white,
size: 43,
),
const SizedBox(width: 33),
SelectTransactionItem(
choiceId == '0' //id of new outcome
? '-${oCcyy.format(0)}'
: '${oCcyy.format(0)}',
_amountController,
false,
_colorCorrect),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
IconPicked(_scaffoldKey, choiceId),
const SizedBox(
width:
14),
SelectTransactionItem(
'Titel',
_titleController,
true,
_colorCorrect),
],
),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? 'Jede 2 Woche'
: 'Ausgewählter Stuff',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.runningtime, size: 40),
iconSpace: 0,
iconAfterSpace: 20,
onPress: () {}),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? '${DateFormat.yMd(local).format(DateTime.now())}'
: '${DateFormat.yMMMd().format(_selectedDate)}',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.calender, size: 39),
iconSpace: 3,
iconAfterSpace: 18,
onPress: () {
showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return SizedBox(
height: MediaQuery.of(context)
.copyWith()
.size
.height /
3,
child: CupertinoDatePicker(
//backgroundColor: Color(0xffb00374a),
initialDateTime: DateTime.now(),
onDateTimeChanged:
(DateTime pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
},
// use24hFormat: true,
maximumDate: DateTime.now(),
minimumYear: 2010,
maximumYear: 2020,
//minuteInterval: 1,
mode: CupertinoDatePickerMode.date,
),
);
});
},
),
Row(
children: <Widget>[
const SizedBox(width: 3.5),
const Icon(
MyIcon.notes,
color: Colors.white,
size: 37,
),
const SizedBox(width: 20),
SelectTransactionItem('Notizen', _notesController,
false, _colorCorrect),
],
),
Container(
width: 0.1,
height: MediaQuery.of(context).size.height * 0.08,
color: Colors.transparent),
])
: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Row(children: <Widget>[
const SizedBox(width: 3.5),
choiceId == '0'
? const Icon(
MyIcon.paying,
color: Colors.white,
size: 25,
)
: const Icon(
MyIcon.earning,
color: Colors.white,
size: 43,
),
const SizedBox(width: 33),
SelectTransactionItem(
choiceId == '0' //id of new outcome
? '-${oCcyy.format(0)}'
: '${oCcyy.format(0)}',
_amountController,
false,
_colorCorrect),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
IconPicked(_scaffoldKey, choiceId),
const SizedBox(
width:
14),
SelectTransactionItem(
'Titel',
_titleController,
true,
_colorCorrect),
],
),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? 'Jede 2 Woche'
: 'Ausgewählter Stuff',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.runningtime, size: 40),
iconSpace: 0,
iconAfterSpace: 20,
onPress: () {}),
_buildTransactionRow(
comparisonStuff: _selectedDate,
typedText: Text(
_selectedDate == null
? '${DateFormat.yMd(local).format(DateTime.now())}'
: '${DateFormat.yMMMd().format(_selectedDate)}',
style: Theme.of(context).textTheme.title),
icon: const Icon(MyIcon.calender, size: 39),
iconSpace: 3,
iconAfterSpace: 18,
onPress: () {
showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return SizedBox(
height: MediaQuery.of(context)
.copyWith()
.size
.height /
3,
child: CupertinoDatePicker(
//backgroundColor: Color(0xffb00374a),
initialDateTime: DateTime.now(),
onDateTimeChanged:
(DateTime pickedDate) {
if (pickedDate == null) {
return;
}
setState(() {
_selectedDate = pickedDate;
});
},
// use24hFormat: true,
maximumDate: DateTime.now(),
minimumYear: 2010,
maximumYear: 2020,
//minuteInterval: 1,
mode: CupertinoDatePickerMode.date,
),
);
});
},
),
Row(
children: <Widget>[
const SizedBox(width: 3.5),
const Icon(
MyIcon.notes,
color: Colors.white,
size: 37,
),
const SizedBox(width: 20),
SelectTransactionItem('Notizen', _notesController,
false, _colorCorrect),
],
),
Container(
width: 0.1,
height: MediaQuery.of(context).size.height * 0.08,
color: Colors.transparent),
])),
),
),
]);
}
#override
Widget build(BuildContext context) {
var local = Localizations.localeOf(context).toString();
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final _pageTitle = routeArgs['pageTitle'];
final _choiceId = routeArgs['id'];
final transactions = Provider.of<NewTransactions>(context, listen: false);
final kontoss = Provider.of<GeldKonto>(context, listen: false);
final PreferredSizeWidget appBar =
_buildAppBar(_pageTitle, transactions, kontoss, _choiceId);
return
// SafeArea(
// child:
Scaffold(
key: _scaffoldKey,
appBar: appBar,
// body: Stack(
// children: <Widget>[
body: _buildColumn(context, appBar, local, _choiceId),
//),
);
}
}
It is fine when i open the upper textfield but the container overlap when opening the last textfield. I can scroll up to see the notes ,,textfield'', but it looks ugly when I open the notes ,,textfield'' and the thing is overlapping. Is there a way to move the textfield up automatically or just preventing that the Container is pushed up as well?
I tried to put the padding of the textfield column Container to + 10, but it squeezed the whole page.
padding: EdgeInsets.only(
top: 10,
left: 10,
right: 10,
bottom: MediaQuery.of(context).viewInsets.bottom +
10, // so that the whole thing always move +10
),
Do you have any advice to solve the problem?
Try adding resizeToAvoidBottomInset: false to your scaffold
Try wrapping Scaffold widget with a Stack and then put the Container widget after Scaffod
If you need to hide some widgets when keyboard is visible, you can use this package: https://pub.dev/packages/flutter_keyboard_visibility
Example:
KeyboardVisibilityBuilder(builder: (context, isKeyboardVisible) {
return isKeyboardVisible
? const SizedBox()
: const MyButton();
}),
I don't like using resizeToAvoidBottomPadding: false, because, when you have a text field, it doesn't allow to move the text field to the top of keyboard.

Why calling setState in a callback inside child widget, which have another setState, breaks program?

Here is a custom Switch widget I implemented using Animation.
enum SwitchType {
LockToggle, EnableToggle
}
class DiamondSwitch extends StatefulWidget {
final double width, height;
final SwitchType switchType;
final double switchThumbSize;
final VoidCallback onTapCallback;
DiamondSwitch({
key, this.width, this.height,
this.switchType, this.switchThumbSize,
#required this.onTapCallback
}) : super(key: key);
#override
_DiamondSwitchState createState() => _DiamondSwitchState(
width: width, height: height,
switchType: switchType, switchThumbSize: switchThumbSize,
onTapCallback: onTapCallback
);
}
class _DiamondSwitchState extends State<DiamondSwitch> {
final double width, height;
final int _toggleAnimationDuration = 1000;
bool _isOn = false;
final List<Color>
_darkGradientShades = <Color>[
Colors.black, Color.fromRGBO(10, 10, 10, 1.0)
],
_lightGradientShades = <Color>[
Colors.white, Color.fromRGBO(150, 150, 150, 1.0)
];
final SwitchType switchType;
final double switchThumbSize;
List<Icon> _switchIcons = new List<Icon>();
final double _switchIconSize = 35.0;
final VoidCallback onTapCallback;
_DiamondSwitchState({
this.width = 100.0, this.height = 40.0,
this.switchThumbSize = 40.0, #required this.switchType,
#required this.onTapCallback
});
#override
void initState() {
_switchIcons.addAll(
(switchType == SwitchType.LockToggle)?
<Icon>[
Icon(
Icons.lock,
color: Colors.black,
size: _switchIconSize,
),
Icon(
Icons.lock_open,
color: Colors.white,
size: _switchIconSize,
)
]
:
<Icon>[
Icon(
Icons.done,
color: Colors.black,
size: _switchIconSize,
),
Icon(
Icons.close,
color: Colors.white,
size: _switchIconSize,
)
]
);
super.initState();
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
duration: Duration(milliseconds: _toggleAnimationDuration),
width: width, height: height,
decoration: ShapeDecoration(
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(28.0),
side: (_isOn)?
BorderSide(
color: Color.fromRGBO(45, 45, 45, 1.0),
width: 0.5,
)
:
BorderSide.none,
),
gradient: LinearGradient(
colors: <Color>[
...((_isOn)? _darkGradientShades : _lightGradientShades)
],
begin: Alignment(1.0, -0.8), end: Alignment(-0.7, 1.0),
stops: <double>[0.4, 1.0]
),
),
alignment: Alignment(0.0, 0.0),
child: Stack(
alignment: Alignment(0.0, 0.0),
children: <Widget>[
AnimatedPositioned(
duration: Duration(milliseconds: _toggleAnimationDuration),
curve: Curves.easeIn,
left: (_isOn)? 0.0 : ((width * 70) / 100),
right: (_isOn)? ((width * 70) / 100) : 0.0,
child: GestureDetector(
onTap: () {
onTapCallback();
setState(() {
_isOn = !_isOn;
});
},
child: AnimatedSwitcher(
duration: Duration(milliseconds: _toggleAnimationDuration),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: child,
);
},
child: Transform.rotate(
alignment: Alignment.center,
angle: (math.pi / 4),
child: Container(
width: switchThumbSize, height: switchThumbSize,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(3.0),
color: (_isOn)? Colors.white : Colors.black,
border: (_isOn)?
null
:
Border.all(
color: Color.fromRGBO(87, 87, 87, 1.0),
width: 1.0,
),
),
child: Transform.rotate(
alignment: Alignment.center,
angle: -(math.pi / 4),
child: (_isOn)?
_switchIcons[0]
:
_switchIcons[1],
),
),
),
),
),
)
],
),
);
}
}
I added a onTapCallback because I need to set another flag in the parent widget to trigger a Image change. Here is related code that belongs to parent widget;
DiamondSwitch(
switchType: SwitchType.LockToggle,
width: 186.0,
height: 60.0,
switchThumbSize: 41.0,
onTapCallback: () {
this.setState(() {
this._isLockOn = !this._isLockOn;
});
},
key: UniqueKey(),
),
When I run this code, animation doesn't work. It detects tap and executes onTap callback, and all code in onTap Works, (I tested with print methods), but as I said, animation isn't happening.
I want to learn why does this happen, is this about how Flutter work? If yes, can you explain?
TY for taking time ^.^!
EDIT
I want to know why does getting a method with setState breaks the animation I'm sharing the current parent widget with the approach of the answerer #pulyaevskiy implemented.
class _SettingsState extends State<Settings> {
bool
_isLockOn = false,
_isPassiconOn = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SafeArea(
child: Column(
children: <Widget>[
/*Lock Toggle Graphic*/
Align(
alignment: Alignment(-0.1, 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
/*Lock Toggle Text*/
RotatedBox(
quarterTurns: 3,
child: Column(
children: <Widget>[
Text(
(_isLockOn)? "locked" : "unlocked",
style: TextStyle(
color: Colors.white,
fontFamily: "Philosopher",
fontSize: 25.0,
shadows: <Shadow>[
Shadow(
color: Color.fromRGBO(184, 184, 184, 0.68),
offset: Offset(2.0, 2.0),
blurRadius: 4.0,
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 5.5),
child: Container(
color: Color.fromRGBO(204, 204, 204, 1.0),
width: 30.0, height: 1.0,
),
),
],
),
),
/*Lock Toggle Image*/
Image.asset(
"assets/images/settings_screen/crystal_"
"${(_isLockOn)? "white_light_up" : "black_outline"}.png",
scale: 5.0,
alignment: Alignment.center,
),
],
),
),
/*Lock Toggle Switch*/
Padding(
padding: const EdgeInsets.only(top: 12.5),
child: DiamondSwitch(
switchType: SwitchType.LockToggle,
width: 186.0,
height: 60.0,
switchThumbSize: 41.0,
flagToControl: this._isLockOn,
onTapCallback: () {
this.setState(() {
this._isLockOn = !this._isLockOn;
});
},
key: UniqueKey(),
),
),
/*Separator*/
WhiteDiamondSeparator(paddingAmount: 36.5),
/*Passicon Toggle Graphic*/
Align(
alignment: Alignment(-0.32, 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
/*Lock Toggle Text*/
RotatedBox(
quarterTurns: 3,
child: Column(
children: <Widget>[
Text(
"passicon",
style: TextStyle(
color: Colors.white,
fontFamily: "Philosopher",
fontSize: 25.0,
shadows: <Shadow>[
Shadow(
color: Color.fromRGBO(184, 184, 184, 0.68),
offset: Offset(2.0, 2.0),
blurRadius: 4.0,
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 5.5),
child: Container(
color: Color.fromRGBO(204, 204, 204, 1.0),
width: 30.0, height: 1.0,
),
),
],
),
),
/*Passicon Toggle Image*/
Padding(
padding: const EdgeInsets.only(left: 40),
child: Image.asset(
"assets/images/settings_screen/emote_"
"${(_isPassiconOn)? "winking" : "nervous"}.png",
scale: 3.25,
alignment: Alignment.center,
),
),
],
),
),
/*Passicon Toggle Switch*/
Padding(
padding: const EdgeInsets.only(top: 42.5),
child: DiamondSwitch(
switchType: SwitchType.PassiconToggle,
width: 186.0,
height: 60.0,
switchThumbSize: 41.0,
flagToControl: this._isPassiconOn,
onTapCallback: () {
this.setState(() {
this._isPassiconOn = !this._isPassiconOn;
});
},
key: UniqueKey(),
),
),
/*Separator*/
WhiteDiamondSeparator(paddingAmount: 36.5),
],
)
),
);
}
}
And I added flagToControl in DiamondSwitch and using it in _DiamondSwitchState as bool get _isOn => widget.flagToControl;.
In the old approach, if I don't execute and other thing than
setState() { _isOn = !_isOn; }
animation happens as it should. What am I missing?
In your onTapCallback you are changing state of your parent widget, which works indeed. However it does not affect state of the DiamondSwitch widget itself
(I see that in GestureDetector you also setState of the switch widget, but there are a few issues with this approach).
To fix this you can pass the value of this._isLockOn from your parent widget's state to the DiamondSwitch child. This means you need another property on your switch widget. E.g.
class DiamondSwitch extends StatefulWidget {
final bool isOn;
// ... remaining fields go here
DiamondSwitch({this.isOn, ...});
}
Then change _DiamondSwitchState as well. Could simply proxy _isOn to the widget's value:
class _DiamondSwitchState extends State<DiamondSwitch> {
bool get _isOn => widget.isOn;
}
This is much better than keeping state of isOn in two places as you have now (in parent widget AND in the switch itself). With this change your isLockOn state is only kept on the parent widget and you just pass it down to the switch child to use.
This means that for GestureDetector's onTap property you'll simply pass the onTapCallback of the parent widget as well, no need to wrap it with another function.
The last part: in your parent widget's build method:
DiamondSwitch(
isOn: this._isLockOn, // <-- new line here to pass the value down
switchType: SwitchType.LockToggle,
width: 186.0,
height: 60.0,
switchThumbSize: 41.0,
onTapCallback: () {
this.setState(() {
this._isLockOn = !this._isLockOn;
});
},
key: UniqueKey(),
),
Another benefit of doing it this way is that now you can initialize your switch with a different default value if needed (right now it's hardcoded to always start off as false). So if you load value of isLockOn from a database and it's set to true you can immediately pass this value to the switch child and represent your state correctly.