Add and remove columns childs inside Gridview - android

I write the code to add and remove columns inside Gridview at first load screen everything looks fine but when I remove any column on click the cross button inside the column it breaks the layout and does not remove any column whenever i click to remove column it shows error
Error: SliverGeometry is not valid: The "maxPaintExtent" is less than the "paintExtent".
Error Image: Error of layout during click/remove item
Here is my code :-
import 'package:flutter/material.dart';
import 'package:mindmatch/utils/widget_functions.dart';
import 'package:mindmatch/custom/BorderIcon.dart';
import 'package:mindmatch/screens/Relation.dart';
import 'package:flutter_svg/flutter_svg.dart';
void main() {
runApp(new MaterialApp(
home: new Photos(),
));
}
class Photos extends StatefulWidget {
var usrid;
Photos({Key? key, #required this.usrid}) : super(key: key);
#override
_Photos createState() => _Photos();
}
class _Photos extends State<Photos>{
int counter = 0;
//List<Widget> _list = new List<Widget>();
List<Widget> _list = <Widget> [];
#override
void initState() {
for(int i =0; i < 4; i++){
Widget child = _newItem(i);
_list.add(child);
}
}
void on_Clicked() {
Widget child = _newItem(counter);
setState(() => _list.add(child));
}
Widget _newItem(int i) {
Key key = new Key('item_${i}');
Column child = Column(
key: key,
children: [
Stack(
children: [
Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Color(0xffa1a1a1),
),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child: SizedBox(
//width: 300,
height: 100,
child: Center(child:
Icon(
Icons.image,
color: Color(0xffcccccc),
size: 60,
),
),
),
),
Positioned(
top: 9,
right: 9,
child: InkWell(
onTap: () => _removeItem(key),
child: SvgPicture.asset(
width: 20,
'assets/images/close.svg',
height: 20,
),
),
)
]
),
]
);
counter++;
return child;
}
void _removeItem(Key key){
for(int i = 0; i < _list.length; i++){
Widget child = _list.elementAt(i);
if(child.key == key){
setState(() => _list.removeAt(i));
print('Removing ${key.toString()}');
}
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Photos'),
backgroundColor: Colors.deepPurpleAccent,
),
floatingActionButton: new FloatingActionButton(onPressed: on_Clicked, child: new
Icon(Icons.add),),
body: new Container(
padding: new EdgeInsets.all(32.0),
child: Column(
children: [
Expanded(
child: GridView(
//padding: const EdgeInsets.all(8.0),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10.0,
mainAxisSpacing: 15,
//childAspectRatio: 2/1,
),
children: _list,
)
)
],
),
),
);
}
}
And here is my output:- Output image of code where clearly see add and remove functionality
Please help out how I did this task and how I add and remove columns inside Gridview
anyone, please help me.
And this is what I need to do:- this is actual layout i want to create
So please help me

You should not operate the widget directly but data instead. What you should store in the list is the data (image URL or some string, which can be a very complex data structure) you want to present in the view and make the view respond to the changes happening on the object list with flutter's setState function.
try this:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new Photos(),
));
}
class Photos extends StatefulWidget {
var usrid;
Photos({Key? key, #required this.usrid}) : super(key: key);
#override
_Photos createState() => _Photos();
}
class _Photos extends State<Photos> {
int counter = 0;
//List<Widget> _list = new List<Widget>();
List<String> _list = <String>[];
#override
void initState() {
for (int i = 0; i < 4; i++) {
_list.add("your data -obj");
}
}
void on_Clicked() {
setState(() => _list.add("your data -obj"));
}
Widget _newItem(int i) {
Key key = new Key('item_${i}');
Column child = Column(key: key, children: [
Stack(children: [
Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Color(0xffa1a1a1),
),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child: SizedBox(
//width: 300,
height: 80,
child: Center(
child: Icon(
Icons.image,
color: Color(0xffcccccc),
size: 60,
),
),
),
),
Positioned(
top: 9,
right: 9,
child: InkWell(onTap: () => _removeItem(i), child: Text('close')),
)
]),
]);
counter++;
return child;
}
void _removeItem(int i) {
print("====remove $i");
print('===Removing $i');
setState(() => _list.removeAt(i));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Photos'),
backgroundColor: Colors.deepPurpleAccent,
),
floatingActionButton: new FloatingActionButton(
onPressed: on_Clicked,
child: new Icon(Icons.add),
),
body: new Container(
padding: new EdgeInsets.all(32.0),
child: Column(
children: [
Expanded(
child: GridView(
//padding: const EdgeInsets.all(8.0),
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10.0,
mainAxisSpacing: 15,
//childAspectRatio: 2/1,
),
children: List.generate(_list.length, (index) {
//generating tiles with people from list
return _newItem(index);
})))
],
),
),
);
}
}

Related

Flutter- Can border radius be given to Expansion Panel list? If yes, what are the properties that should be used

I have this code for expansion panel list which is working fine but I am not able to extend the size of the expansion panel. Also, I want the expansion panel to have a border radius but I am not sure if border radius can be given
return Column(children: [
Stack(
clipBehavior: Clip.none,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 50),
child: ExpansionPanelList(
animationDuration: Duration(milliseconds: 1000),
children: [
ExpansionPanel(
headerBuilder: (context, isExpanded) {
return Column(
children: [
Text("Salmon Poké"),
Text("Rs. 2000"),
],
);
},
body: Text(
'This salmon sashimi is a delicious light appetizer served with fresh wasabi, ginger, soy sauce or a delicious side of soy yuzo citrus ponzu.',
style: TextStyle(
fontSize: 18,
color: Colors.black,
),
),
isExpanded: _expanded,
canTapOnHeader: true,
backgroundColor: Colors.white),
],
dividerColor: Colors.grey,
expansionCallback: (panelIndex, isExpanded) {
_expanded = !_expanded;
setState(() {});
},
),
),
Positioned(
top: -55,
right: 240,
child: CircleAvatar(
radius: 105,
child: ClipOval(
child: Image(
image: AssetImage('assets/images/salmon.png'),
),
),
backgroundColor: Colors.transparent,
),
),
],
),
]);
}
}
**Here is the UI for the given code **
This is the output I have.
Here is the output which I want. (This UI is made using container widgets but I want this layout using expansion panel list)
This is the output that I want
I really appreciate your help.
For border radius we need to make custom expansion list
import 'package:flutter/material.dart';
const double _kPanelHeaderCollapsedHeight = 48.0;
const double _kPanelHeaderExpandedHeight = 64.0;
class CustomExpansionPanelList extends StatelessWidget {
const CustomExpansionPanelList(
{Key key,
this.children: const <ExpansionPanel>[],
this.expansionCallback,
this.animationDuration: kThemeAnimationDuration})
: assert(children != null),
assert(animationDuration != null),
super(key: key);
final List<ExpansionPanel> children;
final ExpansionPanelCallback expansionCallback;
final Duration animationDuration;
bool _isChildExpanded(int index) {
return children[index].isExpanded;
}
#override
Widget build(BuildContext context) {
final List<Widget> items = <Widget>[];
const EdgeInsets kExpandedEdgeInsets = const EdgeInsets.symmetric(
vertical: _kPanelHeaderExpandedHeight - _kPanelHeaderCollapsedHeight);
for (int index = 0; index < children.length; index += 1) {
if (_isChildExpanded(index) && index != 0 && !_isChildExpanded(index - 1))
items.add(new Divider(
key: new _SaltedKey<BuildContext, int>(context, index * 2 - 1),
height: 15.0,
color: Colors.transparent,
));
final Row header = new Row(
children: <Widget>[
new Expanded(
child: new AnimatedContainer(
duration: animationDuration,
curve: Curves.fastOutSlowIn,
margin: _isChildExpanded(index)
? kExpandedEdgeInsets
: EdgeInsets.zero,
child: new SizedBox(
height: _kPanelHeaderCollapsedHeight,
child: children[index].headerBuilder(
context,
children[index].isExpanded,
),
),
),
),
new Container(
margin: const EdgeInsetsDirectional.only(end: 8.0),
child: new ExpandIcon(
isExpanded: _isChildExpanded(index),
padding: const EdgeInsets.all(16.0),
onPressed: (bool isExpanded) {
if (expansionCallback != null)
expansionCallback(index, isExpanded);
},
),
),
],
);
double _radiusValue = _isChildExpanded(index)? 8.0 : 0.0;
items.add(
new Container(
key: new _SaltedKey<BuildContext, int>(context, index * 2),
child: new Material(
elevation: 2.0,
borderRadius: new BorderRadius.all(new Radius.circular(_radiusValue)),
child: new Column(
children: <Widget>[
header,
new AnimatedCrossFade(
firstChild: new Container(height: 0.0),
secondChild: children[index].body,
firstCurve:
const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn),
secondCurve:
const Interval(0.4, 1.0, curve: Curves.fastOutSlowIn),
sizeCurve: Curves.fastOutSlowIn,
crossFadeState: _isChildExpanded(index)
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: animationDuration,
),
],
),
),
),
);
if (_isChildExpanded(index) && index != children.length - 1)
items.add(new Divider(
key: new _SaltedKey<BuildContext, int>(context, index * 2 + 1),
height: 15.0,
));
}
return new Column(
children: items,
);
}
}
class _SaltedKey<S, V> extends LocalKey {
const _SaltedKey(this.salt, this.value);
final S salt;
final V value;
#override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) return false;
final _SaltedKey<S, V> typedOther = other;
return salt == typedOther.salt && value == typedOther.value;
}
#override
int get hashCode => hashValues(runtimeType, salt, value);
#override
String toString() {
final String saltString = S == String ? '<\'$salt\'>' : '<$salt>';
final String valueString = V == String ? '<\'$value\'>' : '<$value>';
return '[$saltString $valueString]';
}
}
Now use this widget in your application
import 'package:color_essence/customViews/CustomExpansionList.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class ExpansionPanelDemo extends StatefulWidget {
#override
_ExpansionPanelDemoState createState() => _ExpansionPanelDemoState();
}
class _ExpansionPanelDemoState extends State<ExpansionPanelDemo> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Expansion Panel Demo'),
),
body: Container(
padding: EdgeInsets.all(10),
child: ListView.builder(
itemCount: itemData.length,
itemBuilder: (BuildContext context, int index) {
return CustomExpansionPanelList(
animationDuration: Duration(milliseconds: 1000),
children: [
ExpansionPanel(
body: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
padding: EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ClipOval(
child: CircleAvatar(
child: Image.asset(
itemData[index].img,
fit: BoxFit.cover,
),
),
),
SizedBox(
height: 30,
),
Text(
itemData[index].discription,
style: TextStyle(
color: Colors.grey[700],
fontSize: 15,
letterSpacing: 0.3,
height: 1.3),
),
],
),
),
headerBuilder: (BuildContext context, bool isExpanded) {
return Container(
padding: EdgeInsets.all(10),
child: Text(
itemData[index].headerItem,
style: TextStyle(
color: itemData[index].colorsItem,
fontSize: 18,
),
),
);
},
isExpanded: itemData[index].expanded,
)
],
expansionCallback: (int item, bool status) {
setState(() {
itemData[index].expanded = !itemData[index].expanded;
});
},
);
},
),
),
);
}
List<ItemModel> itemData = <ItemModel>[
ItemModel(
headerItem: 'Android',
discription:
"Android is a mobile operating system based on a modified version of the Linux kernel and other open source software, designed primarily for touchscreen mobile devices such as smartphones and tablets. ... Some well known derivatives include Android TV for televisions and Wear OS for wearables, both developed by Google.",
colorsItem: Colors.green,
img: 'assets/images/android_img.png'
),
];
}
class ItemModel {
bool expanded;
String headerItem;
String discription;
Color colorsItem;
String img;
ItemModel({this.expanded: false, this.headerItem, this.discription,this.colorsItem,this.img});
}
Wrap the ExpansionPanelList in a ClipRRect.
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: ExpansionPanelList(
children: [ExpansionPanel(body: Text('Hello World'))])
)

Error Get Value TextEditingController In Flutter

I want to retrieve the value from textEditingController with a loop, the problem that occurs when the listview is not scrolled will result in an error, "RangeError (index): Invalid value: Not in inclusive range 0..23: 24", I have random data of 40 records,
the application will run properly when scrolling to the end of the line. I want even though I don't scroll down the data can still be retrieved without error.
you can run my sample code, please help me thanks.
import 'dart:math';
import 'package:flutter/material.dart';
class Karyawan {
int id;
String nama;
int jamKerja;
Karyawan({this.id, this.nama,});
Karyawan.fromJson(Map<String, dynamic> json) {
id = json['id'];
nama = json['nama_karyawan'];
jamKerja = json['jam_kerja'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['nama_karyawan'] = this.nama;
data['jam_kerja'] = this.jamKerja;
return data;
}
}
List<Karyawan> _daftarKaryawan = List.generate(
40,
(index) => Karyawan(
id: Random().nextInt(100),
nama: 'test ${Random().nextInt(100)}',
),
);
class FormInputAbsenV3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Entry Absen"),
),
body: FormEntry(listKaryawan:_daftarKaryawan)
);
}
}
class FormEntry extends StatefulWidget {
final List<Karyawan> listKaryawan;
const FormEntry({Key key, this.listKaryawan}) : super(key: key);
#override
_FormEntryState createState() => _FormEntryState();
}
class _FormEntryState extends State<FormEntry> {
List karyawan = [];
final _formKey = GlobalKey<FormState>();
List<TextEditingController> _brutos = new List();
List<TextEditingController> _nettos = new List();
void addlistAbsen() {
for (int i = 0; i < widget.listKaryawan.length; i++) {
karyawan.add({
"id": widget.listKaryawan[i].id,
"bruto": _brutos[i].text,
"netto": _nettos[i].text
});
}
print(karyawan);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(5.0),
child: Form(
key: _formKey,
child: Container(
child: ListView.builder(
itemCount: widget.listKaryawan.length,
itemBuilder: (context,index) {
_brutos.add(new TextEditingController());
_nettos.add(new TextEditingController());
return Column(
children: [
FormWidget(
index: index,
nama: widget.listKaryawan[index].nama,
brucon: _brutos[index],
netcon: _nettos[index],
),
SizedBox(
height: 20.0,
),
],
);
},
),
),
),
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.save),
label: Text("Save"),
onPressed: () {
if (_formKey.currentState.validate()) {
addlistAbsen();
}
},
),
);
}
}
class FormWidget extends StatelessWidget {
final int index;
final String nama;
final brucon;
final netcon;
FormWidget({this.index, this.nama, this.brucon, this.netcon});
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text("${index + 1}. ${nama}"),
),
Expanded(
child: TextFormField(
decoration: new InputDecoration(
labelText: "Bruto",
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(),
),
),
style: new TextStyle(
fontFamily: "Poppins",
),
controller: brucon,
),
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
decoration: new InputDecoration(
labelText: "Netto",
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(25.0),
borderSide: new BorderSide(),
),
),
style: new TextStyle(
fontFamily: "Poppins",
),
controller: netcon,
))
],
);
}
}
Please initialize your TextEditingControllers during initState.
#override
void initState() {
super.initState();
for (int i = 0; i < widget.listKaryawan.length; i++) {
_brutos.add(new TextEditingController());
_nettos.add(new TextEditingController());
}
}
Then, remove these lines inside the itemBuilder...
_brutos.add(new TextEditingController());
_nettos.add(new TextEditingController());
NOTE: itemBuilder will be called when a list item is to be redisplayed/newly displayed. Therefore, your list of TextEditingControllers > list items. (try scrolling to the bottom most, then scroll back up)
You CANNOT use ListView.builder for this and you will have to use ListView. You cannot use ListView.builder constructor because the builder is called only for those children that are actually visible. Please see the documentation for ListView.builder
ListView.builder constructor
Creates a scrollable, linear array of widgets that are created on
demand.
This constructor is appropriate for list views with a large (or
infinite) number of children because the builder is called only for
those children that are actually visible.
Please see the following code :
import 'package:flutter/material.dart';
import 'dart:math';
final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
//theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: FormInputAbsenV3(),
),
),
);
}
}
class Karyawan {
int id;
String nama;
int jamKerja;
Karyawan({
this.id,
this.nama,
});
Karyawan.fromJson(Map<String, dynamic> json) {
id = json['id'];
nama = json['nama_karyawan'];
jamKerja = json['jam_kerja'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['id'] = this.id;
data['nama_karyawan'] = this.nama;
data['jam_kerja'] = this.jamKerja;
return data;
}
}
List<Karyawan> _daftarKaryawan = List.generate(
40,
(index) => Karyawan(
id: Random().nextInt(100),
nama: 'test ${Random().nextInt(100)}',
),
);
class FormInputAbsenV3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Entry Absen"),
),
body: FormEntry(listKaryawan: _daftarKaryawan));
}
}
class FormEntry extends StatefulWidget {
final List<Karyawan> listKaryawan;
const FormEntry({Key key, this.listKaryawan}) : super(key: key);
#override
_FormEntryState createState() => _FormEntryState();
}
class _FormEntryState extends State<FormEntry> {
List karyawan = [];
final _formKey = GlobalKey<FormState>();
List<TextEditingController> _brutos = [];
List<TextEditingController> _nettos = [];
#override
void initState() {
super.initState();
for (int i = 0; i < widget.listKaryawan.length; i++) {
_brutos.add(TextEditingController());
_nettos.add(TextEditingController());
}
}
void addlistAbsen() {
for (int i = 0; i < widget.listKaryawan.length; i++) {
karyawan.add({
"id": widget.listKaryawan[i].id,
"bruto": _brutos[i].text,
"netto": _nettos[i].text
});
}
print(karyawan);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(5.0),
child: Form(
key: _formKey,
child: Container(
// child: ListView.builder(
// itemCount: widget.listKaryawan.length,
// itemBuilder: (context, index) {
// _brutos.add(new TextEditingController());
// _nettos.add(new TextEditingController());
// return Column(
// children: [
// FormWidget(
// index: index,
// nama: widget.listKaryawan[index].nama,
// brucon: _brutos[index],
// netcon: _nettos[index],
// ),
// SizedBox(
// height: 20.0,
// ),
// ],
// );
// },
// ),
child: ListView(
children: [
for (int index = 0; index < widget.listKaryawan.length; index++)
Column(
children: [
FormWidget(
index: index,
nama: widget.listKaryawan[index].nama,
brucon: _brutos[index],
netcon: _nettos[index],
),
SizedBox(
height: 20.0,
),
],
),
],
),
),
),
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.save),
label: Text("Save"),
onPressed: () {
if (_formKey.currentState.validate()) {
addlistAbsen();
}
},
),
);
}
}
class FormWidget extends StatelessWidget {
final int index;
final String nama;
final TextEditingController brucon;
final TextEditingController netcon;
const FormWidget({this.index, this.nama, this.brucon, this.netcon});
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text("${index + 1}. ${nama}"),
),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: "Bruto",
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0),
borderSide: BorderSide(),
),
),
style: TextStyle(
fontFamily: "Poppins",
),
controller: brucon,
),
),
SizedBox(
width: 20.0,
),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: "Netto",
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.0),
borderSide: BorderSide(),
),
),
style: TextStyle(
fontFamily: "Poppins",
),
controller: netcon,
))
],
);
}
}

If you delete widget from list it reset state of all the widgets below it in flutter?

Using floating action button you can add questions, but when you delete particular question it reset state of all the widget below it in the list, everything above it maintains its state. TextField didn't retain data though I'm using TextFieldController. I'm unable to figure out why this is happening.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:kaloory/constants.dart';
class AddQuestionsForTest extends StatefulWidget {
AddQuestionsForTest({Key key}): super(key: key);
#override
_AddQuestionsForTestState createState() => _AddQuestionsForTestState();
}
class _AddQuestionsForTestState extends State<AddQuestionsForTest> {
int count = 1;
List<InputWidget> children;
void removeInputWidget(index){
setState(() {
children.remove(index);
});
}
#override
void initState(){
super.initState();
setState(() {
children = List.generate(count, (int i) => new InputWidget(removeInputWidget, key: ValueKey(i),));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: titleAppbar(title: "Create Test"),
body: Container(
color: backgroundColor,
height: MediaQuery.of(context).size.height,
child: ListView.builder(
itemCount: children.length,
itemBuilder: (context, index) {
final child = children[index];
return child;
},
),
),
floatingActionButton: new FloatingActionButton(
backgroundColor: primaryColor,
splashColor: primaryColor.withOpacity(0.5),
child: new Icon(Icons.add),
onPressed: () {
setState(() {
children.add(InputWidget(removeInputWidget,key: ValueKey(count),));
count = count + 1;
});
},
));
}
}
class InputWidget extends StatefulWidget {
final Function(InputWidget) removeInputWidget;
InputWidget(this.removeInputWidget,{Key key}): super(key: key);
#override
_InputWidgetState createState() => _InputWidgetState();
}
class _InputWidgetState extends State<InputWidget> {
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 30, vertical: 5),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
BuildDropDown(),
Divider(),
IconButton(icon: Icon(Icons.delete_outline), onPressed: (){
widget.removeInputWidget(this.widget);
})
],
));
}
}
class BuildDropDown extends StatefulWidget {
#override
_BuildDropDownState createState() => _BuildDropDownState();
}
class _BuildDropDownState extends State<BuildDropDown> {
TextEditingController ques = TextEditingController();
String newValue;
#override
Widget build( BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new ListTile(
title: Container(
padding: EdgeInsets.all(8),
height: 40,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all()),
child: DropdownButtonHideUnderline(
child: new DropdownButton<String>(
hint: Text('Choose'),
onChanged: (String changedValue) {
newValue = changedValue;
setState(() {
print(newValue);
});
},
value: newValue,
items: <String>[
'Multiple Choice',
'Short Answer Type',
'Long Answer Type'
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList()),
),
),
),
],
),
),
Container(
height: 40,
margin: EdgeInsets.only(top: 10),
padding: EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.black.withOpacity(0.5)),
),
child: TextField(
controller: ques,
decoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
hintText: "Untitled Question",
),
),
),
],
);
}
}
This is because TextField has it's own internal state, and it's not being saved even though you have assigned a key to the parent.
I think the solution is to save the text value somewhere in the widget state, and in build() check if the contents of the controller.text != _currentText, if they don't match, then inject the last known _currentText.

Horizontal Stepper in Flutter

I want to create a horizontal stepper, which is easy I know, but this time, the count of steps should large.
Just to give an example, this is what I am doing for the vertical,
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new HomePage(),
);
}
}
class HomePage extends StatelessWidget{
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
child: new ListView(
children: <Widget>[
new Text("Helllo "),
new Text( " Welcome"),
new Text (" Yaaa0"),
new SimpleWidget(),
],
), ),
);
}
}
class SimpleWidget extends StatefulWidget {
#override
SimpleWidgetState createState() => new SimpleWidgetState();
}
class SimpleWidgetState extends State<SimpleWidget> {
int stepCounter = 0;
List<Step> steps = [];
#override
void initState() {
prepareState();
super.initState();
}
void prepareState(){
for (var i= 0; i<100; i++){
var stepVal = new Step(
title:new Text("Step $i"),
content: new Text("This is the child of $i step"),
isActive: true,
);
steps.add(stepVal);
}
}
#override
Widget build(BuildContext context) {
return new Container(
child: new Stepper(
type: StepperType.vertical,
physics : ClampingScrollPhysics(),
currentStep: this.stepCounter,
steps: steps,
onStepTapped: (step) {
setState(() {
stepCounter = step;
});
},
onStepCancel: () {
setState(() {
stepCounter > 0 ? stepCounter -= 1 : stepCounter = 0;
});
},
onStepContinue: () {
setState(() {
stepCounter < steps.length - 1 ? stepCounter += 1 : stepCounter = 0;
});
},
),
);
}
}
As soon as I try to recreate this in the horizontal mode, it shows nothing. I have tried to make the listView horizontal, I have tried to make the stepper horizontal, both individually and also together. None works. You can try that in the dartpad.
My question :
1. How to make a Stepper in horizontal that is scrollable in the horizontal mode.
2. The content of the Stepper is scrollable , I can see that. Can it be switched off?
use this class
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
class StepProgressView extends StatelessWidget {
final double _width;
final List<String> _titles;
final int _curStep;
final Color _activeColor;
final Color _inactiveColor = HexColor("#E6EEF3");
final double lineWidth = 3.0;
StepProgressView(
{Key key,
#required int curStep,
List<String> titles,
#required double width,
#required Color color})
: _titles = titles,
_curStep = curStep,
_width = width,
_activeColor = color,
assert(width > 0),
super(key: key);
Widget build(BuildContext context) {
return Container(
width: this._width,
child: Column(
children: <Widget>[
Row(
children: _iconViews(),
),
SizedBox(
height: 8,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _titleViews(),
),
],
));
}
List<Widget> _iconViews() {
var list = <Widget>[];
_titles.asMap().forEach((i, icon) {
var circleColor = (i == 0 || _curStep > i + 1) ? _activeColor : _inactiveColor;
var lineColor = _curStep > i + 1 ? _activeColor : _inactiveColor;
var iconColor = (i == 0 || _curStep > i + 1) ? _activeColor : _inactiveColor;
list.add(
Container(
width: 20.0,
height: 20.0,
padding: EdgeInsets.all(0),
decoration: new BoxDecoration(
/* color: circleColor,*/
borderRadius: new BorderRadius.all(new Radius.circular(22.0)),
border: new Border.all(
color: circleColor,
width: 2.0,
),
),
child: Icon(
Icons.circle,
color: iconColor,
size: 12.0,
),
),
);
//line between icons
if (i != _titles.length - 1) {
list.add(Expanded(
child: Container(
height: lineWidth,
color: lineColor,
)));
}
});
return list;
}
List<Widget> _titleViews() {
var list = <Widget>[];
_titles.asMap().forEach((i, text) {
list.add(Text(text, style: TextStyle(color: HexColor("#000000"))));
});
return list;
}
}
declare list and int variable inside class you want to use
final List<String> titles = [TextConstant.CART, TextConstant.ADDRESS, TextConstant.PAYMENT];
int _curStep = 1;
finally use above class
StepProgressView(width: MediaQuery.of(context).size.width,
curStep: _curStep,
color: Color(0xff50AC02),
titles: titles),
try this example, e.g: conf pubspec file: fa_stepper: ^0.0.2, then flutter packages get , after that: using FAStepper constructor, define something like this:
Widget w1(BuildContext context) {
return Scaffold(
// Body
body: Container(
child: FAStepper(
// physics: ClampingScrollPhysics(),
// Using a variable here for handling the currentStep
currentStep: this.currentStep,
// List the steps you would like to have
titleHeight: 120,
steps: mySteps,
// Define the type of Stepper style
// StepperType.horizontal : Horizontal Style
// StepperType.vertical : Vertical Style
type: FAStepperType.horizontal,
titleIconArrange: FAStepperTitleIconArrange.column,
stepNumberColor: Colors.pinkAccent,
// Know the step that is tapped
onStepTapped: (step) {
// On hitting step itself, change the state and jump to that step
setState(() {
// update the variable handling the current step value
// jump to the tapped step
currentStep = step;
});
// Log function call
print("onStepTapped : " + step.toString());
},
onStepCancel: () {
// On hitting cancel button, change the state
setState(() {
// update the variable handling the current step value
// going back one step i.e subtracting 1, until its 0
if (currentStep > 0) {
currentStep = currentStep - 1;
} else {
currentStep = 0;
}
});
// Log function call
print("onStepCancel : " + currentStep.toString());
},
// On hitting continue button, change the state
onStepContinue: () {
setState(() {
// update the variable handling the current step value
// going back one step i.e adding 1, until its the length of the step
if (currentStep < mySteps.length - 1) {
currentStep = currentStep + 1;
} else {
currentStep = 0;
}
});
// Log function call
print("onStepContinue : " + currentStep.toString());
},
)),
);
}
You can create Horizontal Stepper in Flutter without any external package also like by following
This will work fine and use StatefulWidget to put this code inside it (StatefulWidget).
int _currentStep = 0;
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Expanded(
child: Stepper(
type: StepperType.horizontal,
physics: ScrollPhysics(),
currentStep: _currentStep,
onStepTapped: (step) => tapped(step),
onStepContinue: continued,
onStepCancel: cancel,
steps: <Step>[
Step(
title: new Text(''),
content: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Email Address'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
),
],
),
isActive: _currentStep >= 0,
state: _currentStep >= 0 ?
StepState.complete : StepState.disabled,
),
Step(
title: new Text(''),
content: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Home Address'),
),
TextFormField(
decoration: InputDecoration(labelText: 'Postcode'),
),
],
),
isActive: _currentStep >= 0,
state: _currentStep >= 1 ?
StepState.complete : StepState.disabled,
),
Step(
title: new Text(''),
content: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Mobile Number'),
),
],
),
isActive:_currentStep >= 0,
state: _currentStep >= 2 ?
StepState.complete : StepState.disabled,
),
Step(
title: new Text(''),
content: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(labelText: 'Mobile Number'),
),
],
),
isActive:_currentStep >= 0,
state: _currentStep >= 3 ?
StepState.complete : StepState.disabled,
),
],
),
),
],
),
);
}
tapped(int step){
setState(() => _currentStep = step);
}
continued(){
_currentStep < 3 ?
setState(() => _currentStep += 1): null;
}
cancel(){
_currentStep > 0 ?
setState(() => _currentStep -= 1) : null;
}
Wrap the stepper with a ConstrainedBox and set its height to a constant and make the StepperType as horizontal. You can check it in dartpad .
return ConstrainedBox(
constraints: BoxConstraints.tightFor(height: 500.0),
child: Stepper(
type: StepperType.horizontal,
),
);
There is an issue about this on github https://github.com/flutter/flutter/issues/40601
BUT
This is what i m using right now
output image
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme:ThemeData(
primarySwatch:Colors.amber
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class CustomStep {
final String title;
final Widget page;
CustomStep(
{#required this.title, #required this.page});
}
class MyWidget extends StatefulWidget {
const MyWidget({ Key key }) : super(key: key);
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
ScrollController _scrollController = new ScrollController();
static const double STEP_WIDTH = 90;
PageController pageController = PageController();
List<CustomStep> stepsList;
int currentPage=0;
#override
void initState() {
super.initState();
stepsList = [
CustomStep(
title: 'ddddd',
page: Placeholder(
color: Colors.pink,
),
),
CustomStep(
title: 'zzzzzzzz',
page: Placeholder(
color: Colors.deepPurple,
),
),
];
}
SizedBox buildStepDivider(int index) {
return SizedBox(
height: 90,
child: Container(
alignment: Alignment.topCenter,
child: Transform.translate(
offset: Offset(0, 16),
child: Container(
color: index < currentPage
? Theme.of(context).primaryColor
: Colors.grey,
width: 30,
height: 3,
padding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
);
}
buildStep(int index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: SizedBox(
height: 90,
width: STEP_WIDTH,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: index <= currentPage
? Theme.of(context).primaryColor
: Colors.grey[300],
),
padding: EdgeInsets.all(10),
child: Text((index + 1).toString()),
),
Expanded(
child: Text(
stepsList[index].title,
textAlign: TextAlign.center,
))
],
),
),
);
}
_buildStepper(int currentStep) {
Future.delayed(
Duration(milliseconds: 100),
() => _scrollController.animateTo((STEP_WIDTH * currentStep).toDouble(),
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut));
return Center(
child: SizedBox(
height: 110,
child: ListView.builder(
controller: _scrollController,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: stepsList.length,
itemBuilder: (ctx, index) => index < stepsList.length - 1
? Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
buildStep(index),
buildStepDivider(index)
],
)
:Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
buildStep(index)]) ),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('hello'), centerTitle: true),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_buildStepper(currentPage),
Expanded(
child: PageView.builder(
controller: pageController,
physics: NeverScrollableScrollPhysics(),
onPageChanged: (index) {
setState(() {
currentPage = index;
});
},
itemCount: stepsList.length,
itemBuilder: (ctx, index) =>
stepsList[index].page,
)),
],
),
);
}
}
I am sure you have got the answer, but maybe this is for someone who is looking for a package instead of creating a custom one. Here is something that I found good, please do check out and see if it fits in your use-case.
https://pub.dev/packages/im_stepper
A very easy step to create a number stepper is
Container(
margin: const EdgeInsets.only(top: 4, right: 6),
padding: const EdgeInsets.all(3.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.red, width: 2),
borderRadius: BorderRadius.circular(2),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
child: Icon(Icons.remove, color: Colors.red),
onTap: _dicrement,
),
Container(
margin: EdgeInsets.only(right: 8, left: 8),
child: Text(
_currentCount.toString(),
style: TextStyle(fontWeight: FontWeight.bold),
),
),
InkWell(
child: Icon(Icons.add, color: Colors.red),
onTap: _increment,
),
],
),
),

Only showing the front side when pressing the nextButton of a flash card app using the flip_card dependency [FLUTTER]

I have a flashcard app using flutter in android studio, I am very new at android studio and flutter. So every time the user flips, it shows the back side of the card. If the card is showing the back side while the user is pressing the next button, the card automatically shows the back side of the proceeding cards. What i wanted to do is only show the front side each time the user presses the next button.
I have 3 classes, a flip_card.dart [my animations], Flash.dart [buttons, widgets, front/back image lists, etc.], main.dart [main, only calls the class].
I do not know what to do, either call the animation when it is played to a function? or to call a local "bool isFront" inside the animation when it is played.
this is my whole flip_dart.dart
library flip_card;
import 'dart:math';
import 'package:flutter/material.dart';
import 'Flash.dart';
enum FlipDirection {
VERTICAL,
HORIZONTAL,
}
class AnimationCard extends StatelessWidget {
AnimationCard({this.child, this.animation, this.direction});
final Widget child;
final Animation<double> animation;
final FlipDirection direction;
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
var transform = Matrix4.identity();
transform.setEntry(3, 2, 0.001);
if (direction == FlipDirection.VERTICAL) {
transform.rotateX(animation.value);
} else {
transform.rotateY(animation.value);
}
return Transform(
transform: transform,
alignment: Alignment.center,
child: child,
);
},
child: child,
);
}
}
class FlipCard extends StatefulWidget {
final Widget front;
final Widget back;
final int speed = 500;
final FlipDirection direction;
const FlipCard(
{Key key,
#required this.front,
#required this.back,
this.direction = FlipDirection.HORIZONTAL})
: super(key: key);
#override
State<StatefulWidget> createState() {
return _FlipCardState();
}
}
class _FlipCardState extends State<FlipCard>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> _frontRotation;
Animation<double> _backRotation;
bool isFront = true;
#override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(milliseconds: widget.speed), vsync: this);
_frontRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween(begin: 0.0, end: pi / 2)
.chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: ConstantTween<double>(pi / 2),
weight: 50.0,
),
],
).animate(controller);
_backRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: ConstantTween<double>(pi / 2),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: Tween(begin: -pi / 2, end: 0.0)
.chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
],
).animate(controller);
}
_toggleCard() {
if (isFront) {
controller.forward();
// if(_nextImage()){
//
// }
} else {
controller.reverse();
}
isFront = !isFront;
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _toggleCard,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
AnimationCard(
animation: _frontRotation,
child: widget.front,
direction: widget.direction,
),
AnimationCard(
animation: _backRotation,
child: widget.back,
direction: widget.direction,
),
],
),
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
And this is my Flash.dart
import 'flip_card.dart';
import 'package:flutter/material.dart';
class Flashcard extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<Flashcard> {
int photoIndex = 0; //startfirstpage
bool isFront = true;
var playerProgress = 0;
List<String> photos = [ //front
'assets/c1v1f.png',
'assets/c1v2f.png',
'assets/c1v3f.png',
'assets/c1v4f.png',
'assets/c1v5f.png',
'assets/c1v6f.png',
'assets/c1v7f.png',
'assets/c1v8f.png',
'assets/c1v9f.png',
'assets/c1v10f.png',
];
List<String> photos1 = [ //back
'assets/c1v1b.png',
'assets/c1v2b.png',
'assets/c1v3b.png',
'assets/c1v4b.png',
'assets/c1v5b.png',
'assets/c1v6b.png',
'assets/c1v7b.png',
'assets/c1v8b.png',
'assets/c1v9b.png',
'assets/c1v10b.png',
];
var bookmarkicon = "assets/bookmarkoff.png";
var bookmarkOff = "assets/bookmarkoff.png";
var bookmarkOn = "assets/bookmarkon.png";
void _previousImage() { // prev
setState(() {
photoIndex = photoIndex > 0 ? photoIndex - 1 : 0;
});
}
void _nextImage() {
// next
setState(() {
photoIndex = photoIndex < photos.length - 1 ? photoIndex + 1 : photoIndex;
playerProgress += 10;
print(playerProgress);
print(photoIndex);
// if(isFront){
// photoIndex--;
// }
Flashcard();
});
}
#override
Widget build(BuildContext context) {
return new Scaffold( //title_page
appBar: new AppBar(
title: new Text('Category 時'),
centerTitle: true,
),
body: Column( //content
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Stack(
children: <Widget>[
Container(
child: FlipCard(
direction: FlipDirection.HORIZONTAL,
front: Material( //front_side
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
image: DecorationImage(
image: AssetImage(photos[photoIndex]), //images front
fit: BoxFit.cover)),
),
elevation: 20.0,
borderRadius: BorderRadius.all(Radius.circular(40.0)),
),
back: Material( //back_side
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
image: DecorationImage(
image: AssetImage(photos1[photoIndex]), // images back
fit: BoxFit.cover)),
),
elevation: 20.0,
borderRadius: BorderRadius.all(Radius.circular(40.0)),
),
),
height: 400.0,//flashcard
width: 370.0,
),
Positioned(
top: 380.0, //dots
left: 25.0,
right: 25.0,
child: SelectedPhoto(numberOfDots: photos.length,
photoIndex: photoIndex),
)
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Prev', textScaleFactor: 1.3,),
onPressed: _previousImage,
elevation: 10.0,
color: Colors.redAccent,
textColor: Colors.white,
),
SizedBox(width: 10.0), //space
ButtonTheme( //bookmark
minWidth: 10,
height: 10,
child: RaisedButton(
child: Image.asset(bookmarkicon, scale: 3.5,),
color: Colors.transparent,
onPressed: (){
if(this.bookmarkicon=='assets/bookmarkoff.png'){
print(this.bookmarkicon +" is clicked");
setState(() {
bookmarkicon = bookmarkOn;
});
this.bookmarkicon = 'assets/bookmarkon.png';
}
else if(this.bookmarkicon=='assets/bookmarkon.png'){
print(this.bookmarkicon +" is clicked");
this.bookmarkicon = 'assets/bookmarkoff.png';
setState(() {
bookmarkicon = bookmarkOff;
});
}
//any implementation for bookmark will be here ;)
},
),
),
SizedBox(width: 10.0), //space
RaisedButton(
child: Text('Next', textScaleFactor: 1.3,),
onPressed:_nextImage,
elevation: 10.0,
color: Colors.redAccent,
textColor: Colors.white,
)
],
)
],
));
}
}
class SelectedPhoto extends StatelessWidget {
final int numberOfDots;
final int photoIndex;
SelectedPhoto({this.numberOfDots, this.photoIndex});
Widget _inactivePhoto() {
return new Container(
child: new Padding(
padding: const EdgeInsets.only(left: 3.0, right: 3.0),
child: Container(
height: 8.0,
width: 8.0,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(4.0)
),
),
)
);
}
Widget _activePhoto() {
return Container(
child: Padding(
padding: EdgeInsets.only(left: 3.0, right: 3.0),
child: Container(
height: 10.0,
width: 10.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5.0),
boxShadow: [
BoxShadow(
color: Colors.grey,
spreadRadius: 0.0,
blurRadius: 2.0
)
]
),
),
),
);
}
List<Widget> _buildDots() {
List<Widget> dots = [];
for(int i = 0; i< numberOfDots; ++i) {
dots.add(
i == photoIndex ? _activePhoto(): _inactivePhoto()
);
}
return dots;
}
#override
Widget build(BuildContext context) {
return new Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: _buildDots(),
),
);
}
}
The code works, but when the user pressed next, it will display the next card even though it is the back side showing.
So here's what I added to my code to make sure every time the user decides to see the next card, it shows the front side of the next card, not the back side:
First I defined a global variable:
GlobalKey<FlipCardState> cardKey = GlobalKey<FlipCardState>();
then it's passed to the key property of flip_card package:
FlipCard(
key: cardKey,
Then I use the variable to check the status of card whether the front of card is shown or the back, if the back side is shown then it will be toggled to show the front side of the next card when clicking on the "next card button":
onPressed: () {
if(cardKey.currentState != null) { //null safety
if (!cardKey.currentState!.isFront) {
cardKey.currentState!.toggleCard();
}
}
},
Adding to the onPressed property of the "next card" button.
Never mind, i just made it into one .dart and made my animator controller into global, and accessed it to .reverse() each time the next button is pressed.

Categories

Resources