I'm having trouble managing the status of my dropdowns,
I have two dropDowns, the items of the second are built based on the item selected in the first.
My problem is being to clean the second one when I change the option of the first one again.
I tried to set the value of the second to null at the time if I update the value of the first but even though it is still giving problem.
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobicar/app/stores/brand_store.dart';
import 'package:mobicar/app/stores/vehicle_store.dart';
class HomePage extends StatefulWidget {
HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
var brandStore = BrandStore();
var vehicleStore = VehicleStore();
var selectedBrand;
var selectedVehicle;
#override
void initState() {
brandStore.getBrands().then((value) {});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: _body(),
);
}
Column _body() {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () => _newItemDialog(),
child: Container(
margin: EdgeInsets.only(top: 5, right: 5),
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: Text(
"new",
style: TextStyle(color: Colors.white),
),
),
),
],
),
Expanded(
child: ListView(
children: [Text("Supervisor, selecione a viatura")],
))
],
);
}
Future _newItemDialog() {
return showDialog(
barrierDismissible: false,
context: context,
builder: (context) => Container(
child: AlertDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Novo Veiculo "),
IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.pop(context))
],
),
content: Observer(builder: (_) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset("assets/images/car.png"),
_dropdownButton(
hint: "Marca",
itemlist: _dropDownItemBrands,
type: 'brand'),
_dropdownButton(
hint: "Modelo",
itemlist: _dropDownItemVehicles,
type: 'vehicle'),
_dropdownButton(
hint: "Ano", itemlist: _dropDownItemBrands, type: ''),
],
);
}),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26),
),
),
));
}
_dropdownButton({required hint, required itemlist, required type}) {
return Container(
margin: EdgeInsets.only(top: 10),
child: DropdownButtonFormField(
hint: Text(hint),
items: itemlist() ?? [],
onChanged: (value) {
switch (type) {
case 'brand':
vehicleStore.getVehicles(value);
break;
case 'vehicle':
selectedVehicle = value;
break;
default:
}
},
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 8, right: 0, top: 0, bottom: 0),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(4)),
),
),
);
}
List<DropdownMenuItem<int>> _dropDownItemBrands() {
List<DropdownMenuItem<int>> list = [];
if (brandStore.brandList.isNotEmpty) {
brandStore.brandList.forEach((element) {
list.add(
DropdownMenuItem<int>(
child: Text(
element.name,
style: TextStyle(color: Colors.black),
),
value: element.id,
),
);
});
return list;
} else {
return list;
}
}
List<DropdownMenuItem<int>> _dropDownItemVehicles() {
List<DropdownMenuItem<int>> list = [];
if (brandStore.brandList.isNotEmpty) {
vehicleStore.vehiclesList.forEach((element) {
list.add(
DropdownMenuItem<int>(
child: Text(
element.name,
style: TextStyle(color: Colors.black),
),
value: element.id,
),
);
});
return list;
} else {
return list;
}
}
}
When you want to change clean the values of the second dropdown menu on your UI, you have to use setState.
So,
I tried to set the value of the second to null at the time if I update
the value of the first but even though it is still giving problem.
Set it to null or an empty list [], but inside of setState:
//instead of yourSecondValue == null, use:
setState(() {
yourSecondValue == null;
});
This is assuming that setting it to null will actually solve the problem.
Related
I ma trying to display the date and time selected from the datepicker widgets and the widgets are also dynamically generated. When the widget is not dynamically generated at that time visibility widgets work but after using in dynamic listview it does not work.
I am expecting to initally display replacement text and after the date and time is selected respctively display the child text.
Here is the code:
import 'package:flutter/material.dart';
import 'package:invoice/app/ui/shared/values/colors/app_colors.dart';
import 'package:invoice/app/ui/widgets/button_widget.dart';
import 'package:table_calendar/table_calendar.dart';
class TimeAndDatePicker extends StatefulWidget {
TimeAndDatePicker({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _TimeAndDatePickerState();
}
}
class _TimeAndDatePickerState extends State<TimeAndDatePicker> {
DateTime _focusedDay = DateTime.now();
TimeOfDay _focusedTime = TimeOfDay.now();
var _isVisibleDate = true;
var _isVisibleTime = true;
#override
void initState() {
super.initState();
}
List<Widget> _cardList = [];
void _addCardWidget() {
setState(() {
_cardList.add(_card());
});
}
Widget _card() {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Visibility(
visible: !_isVisibleDate,
replacement: const Text("Please Select a Date"),
child: Text("Selected Date: "
"${_focusedDay.day}/${_focusedDay.month}/${_focusedDay.year}"
),
),
const SizedBox(height: 8.0),
ButtonWidget(
title: 'Select Date',
onPressed: _showDatePicker,
hasBorder: true,
),
const SizedBox(height: 8.0),
Visibility(
visible: !_isVisibleTime,
replacement: const Text("Please Select a Time"),
child: Text("Selected Time: ${_focusedTime.format(context)}"
),
),
const SizedBox(height: 8.0),
ButtonWidget(
title: 'Select Time',
onPressed: _showTimePicker,
hasBorder: true,
),
const SizedBox(height: 8.0),
],
);
}
void _showDatePicker() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2010),
lastDate: DateTime(2030),
locale: const Locale('en', 'AU'),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: const ColorScheme.light(
primary: AppColors.colorPrimary, // <-- SEE HERE
onPrimary: Colors.white, // <-- SEE HERE
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
primary: Colors.red, // button text color
),
),
),
child: child!,
);
},
).then((value) {
setState(() {
_isVisibleDate = false;
_focusedDay = value??DateTime.now();
});
print("Hello: $value $_focusedDay");
});
}
void _showTimePicker() {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
colorScheme: const ColorScheme.light(
primary: AppColors.colorPrimary, // <-- SEE HERE
onPrimary: Colors.white, // <-- SEE HERE
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
primary: Colors.red, // button text color
),
),
),
child: child!,
);
},
).then((value) {
setState(() {
_isVisibleTime = false;
_focusedTime = value??TimeOfDay.now();
});
print("Time: $value $_focusedTime");
});
}
#override
Widget build(BuildContext context) {
List<DateTime> datelist = [];
return Scaffold(
appBar: AppBar(
title: const Text('Time and Date Picker',
style: TextStyle(
color: Colors.white,
)),
),
body: Padding(
padding: const EdgeInsets.all(15.0),
child:
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: _cardList.length,
itemBuilder: (context, index) {
return _cardList[index];
}
),
),
ButtonWidget(
title: 'Add more?',
onPressed: _addCardWidget,
hasBorder: true,
),
const SizedBox(height: 8.0),
],
),
),
);
}
}
I'm trying to make a slider with the carousel_slider package. Image:
When I press the button written Next, I want it to go to the next page. I use it as it says in the document, but I get an error. Document
The error I got, It first redirects to a file named carousel_controller.dart, and then gives this error:
_CastError (Null check operator used on a null value)
Codes:
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:getwidget/getwidget.dart';
final CarouselController _controller = CarouselController();
class selamlasmaLearn extends StatefulWidget {
#override
State<selamlasmaLearn> createState() => _selamlasmaLearnState();
}
class _selamlasmaLearnState extends State<selamlasmaLearn> {
List<wordAndMeaning> wordsList = [
wordAndMeaning("Hello", "Merhaba", false),
wordAndMeaning("Go", "Gehen", false)
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
body: Builder(builder: (context) {
final double height = MediaQuery.of(context).size.height - 75;
return Column(
children: [
CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
),
items: wordsList.map((wordAndMeaning word) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.amber),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(word.word,
style:
TextStyle(fontSize: 45, color: Colors.white)),
if (word.showMeaning) ...[
Text(word.meaning,
style: TextStyle(
fontSize: 20, color: Colors.white))
]
],
),
const SizedBox(
width: 10,
),
IconButton(
icon: Icon(Icons.remove_red_eye_sharp),
color: Colors.white,
iconSize: 25,
onPressed: () {
setState(() {
word.showMeaning = !word.showMeaning;
});
},
),
],
),
);
},
);
}).toList(),
),
Column(
children: [
GFButton(
text: "Next",
onPressed: () => _controller.nextPage( // <<<<<<<<<<
duration: const Duration(),
curve: Curves.easeInCirc),
)
],
)
],
);
}),
);
}
}
class wordAndMeaning {
String word;
String meaning;
bool showMeaning;
wordAndMeaning(this.word, this.meaning, this.showMeaning);
}
I marked the line that gave the error.
The line causing the error:
onPressed: () => _controller.nextPage(
How can I solve it? Thanks in advance for the help.
You need to assign your CarouselController to your CarouselSlider
CarouselSlider(
controller: _controller,
//...
)
Also, You should define your CarouselController inside your state class
class _selamlasmaLearnState extends State<selamlasmaLearn> {
final CarouselController _controller = CarouselController();
//...
}
(Friendly Advice: please always name your classes & variables in English and a class should always start with a capital letter)
I faced the same problem. Here is how I fixed it:
go to carousel_controller.dart file
search for "nextPage" method
inside this method, change "isNeedResetTimer" from:
final bool isNeedResetTimer = _state!.options.pauseAutoPlayOnManualNavigate;
to:
final bool isNeedResetTimer = true;
This is a carousel slider bug... _state is undefined.
Have started working with flutter and have encountered this problem would be glad if assisted.
Have a code like this:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'genderBtns_content.dart';
import 'reusableContainer_cards.dart';
const bottomContainerHeight = 80.0;
const activeCardColor = Color(0xFF1D1E33);
const inactiveCardColor = Color(0xFF111328);
class InputPage extends StatefulWidget {
#override
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
Color maleCardColor = inactiveCardColor;
Color femaleCardColor = inactiveCardColor;
void updateColor(int gender) {
if (gender == 1) {
if (maleCardColor == inactiveCardColor) {
maleCardColor = activeCardColor;
femaleCardColor = inactiveCardColor;
} else {
maleCardColor = inactiveCardColor;
}
} else if (gender == 2) {
if (femaleCardColor == inactiveCardColor) {
femaleCardColor = activeCardColor;
maleCardColor = inactiveCardColor;
} else {
femaleCardColor = inactiveCardColor;
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold (
appBar: AppBar(
title: Text('BMI CALCULATOR'),
),
body: Column(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
updateColor(1);
});
},
child: ContainerReuse(
colour: maleCardColor,
cardChild: GenderColReuse(
icon: FontAwesomeIcons.mars,
label: 'MALE',
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
updateColor(2);
});
},
child: ContainerReuse(
colour: femaleCardColor,
cardChild: GenderColReuse(
icon: FontAwesomeIcons.venus,
label: 'FEMALE',
),
),
),
),
],
),
),
// End of Gender Cards
Expanded(
child: Expanded(
child: ContainerReuse(colour: activeCardColor),
),
),
Expanded(
child: Row(
children: <Widget>[
Expanded(
child: ContainerReuse(
colour: activeCardColor,
),
),
Expanded(
child: ContainerReuse(
colour: activeCardColor,
),
),
],
),
),
Container(
margin: EdgeInsets.only(top: 10.0),
color: Color(0xFFEB1555),
width: double.infinity,
height: bottomContainerHeight,
),
],
),
);
}
}
After running the code everything works fine until when i click on the GestureDetector.Its suppossed to change background color when tapped, it changes alright but then the background color should be removed upon tapping another GestureDetector but when another button pressed it throws an erro: "Each child must be laid out exactly once." referring to line-40 return Scaffold . I really need help because am still learning flutter and am stacked because of this.
I have a problem, I have a main activity where I have loaded several widget classes so far so good.
now what I want to do is refresh the main page after closing a page that has been triggered in a Drawer menu.
It works if the button is directly on the main page, but if the action is triggered from the Drawer menu it does not work.
Example of screen or it works very well
Option 2
It should look like this. but it doesn't work when I call the page from the Drawer menu
reference link:
How to go back and refresh the previous page in Flutter?
How to refresh a page after back button pressed
Would anyone have an idea.
Here is the code to use for option 1 with the button on the main page:
new RaisedButton(
onPressed: ()=>
Navigator.of(context).push(new MaterialPageRoute(builder: (_)=>new PageHomeContent()),)
.then((val)=>{getRefreshRequests()}),
child: Text('Refresh', style: TextStyle(color: Colors.white), ), color: Colors.purple,
elevation: 2.0,
),
It is important to know that I have created a class for the Drawer menu here. it is a little long but I you essential
final Color primaryColor = Colors.white;
final Color activeColor = Colors.grey.shade800;
final Color dividerColor = Colors.grey.shade600;
class BuildDrawer extends StatefulWidget{
#override
_BuildDrawer createState() => _BuildDrawer();
}
class _BuildDrawer extends State<BuildDrawer> {
//region [ ATTRIUTS ]
final String image = 'https://avatars2.githubusercontent.com/u/3463865?s=460&u=c0fab43e4b105e9745dc3b5cf61e21e79c5406c2&v=4';
List<dynamic> menuGroupList = [];
Future<List<dynamic>> _futureMenuGroupList;
bool _infiniteStop;
//MenuItemGroupModel menuItemGroup = new MenuItemGroupModel();
List<dynamic> menuItemList = [];
Future<List<dynamic>> _futureMenuItemList;
//Future<MenuItemGroupModel> _futureMenuItemGroup;
bool _infiniteItemStop;
//endregion
#override
void initState() {
_futureMenuGroupList = fetchMenuWPList();
_infiniteStop = false;
}
#override
Widget build(BuildContext context) {
return ClipPath(
clipper: OvalRightBorderClipper(),
child: Drawer(
child: Container(
padding: const EdgeInsets.only(left: 16.0, right: 40),
decoration: BoxDecoration(
color: primaryColor,
boxShadow: [BoxShadow(color: Colors.black45)]),
width: 300,
child: SafeArea(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: InkWell(
onTap: () {
//Navigator.push( context, MaterialPageRoute(builder: (context) => PageHomeContent(),),);
Navigator.of(context).push(new MaterialPageRoute(builder: (_)=>new PageHomeContent()),)
.then((val)=>{ new MainPage() });
},
child:
Column(
children: <Widget>[
Row(
children: [
Icon(
Icons.format_list_bulleted,
color: activeColor,
),
SizedBox(width: 10.0),
Text("Home Content", ),
Spacer(),
]
),
],
),
),
),
Divider(
color: dividerColor,
),
],
),
),
),
),
),
);
}
}
//end Class
//region [ MENU ITEM PAGE ]
//endregion
Main Page Class [ MainPage ]
class MainPage extends StatefulWidget {
//MainPage({Key key, this.title}): super(key: key);
//final String title;
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<WPMainPage> {
//region [ ATTRIBUTS ]
List<dynamic> featuredArticles = [];
List<dynamic> latestArticles = [];
List<dynamic> pageList = [];
List<dynamic> menuGroupList = [];
List<dynamic> categoryHomeList = [];
Future<List<dynamic>> _futurePageList;
Future<List<dynamic>> _futureFeaturedArticles;
Future<List<dynamic>> _futureLastestArticles;
Widget widgetCategoryBuilder=new Container();
final _categoryRepository = CategoryRepository();
ScrollController _controller;
int page = 1;
bool _showLoadingPage = true;
bool _showLoadingCategoryHome = true;
bool _infiniteStop;
double heightNoInternet = 280.0;
// Firebase Cloud Messeging setup
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
//endregion
#override
void initState() {
super.initState();
_futureFeaturedArticles = fetchFeaturedArticles(1);
_futureLastestArticles = fetchLatestArticles(1);
_futurePageList = fetchPageList();
getCategoriesOnLocal();
_controller = ScrollController(initialScrollOffset: 0.0, keepScrollOffset: true);
_controller.addListener(_scrollListener);
_infiniteStop = false;
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Constant.APP_NAME_LONG),
actions: getActionAppBarButton(context: context),
),
drawer: BuildDrawer(),
body: Container(
decoration: BoxDecoration(color: Colors.white70),
child: SingleChildScrollView(
controller: _controller,
scrollDirection: Axis.vertical,
child: Column(
children:
getWidgetList()
),
),
));
}
getRefreshRequests() async {
getCategoriesOnLocal();
//Tools.mySnackBar(context, ' m s g TEST 1 ');
}
getWidgetList() {
List<Widget> itemList = new List<Widget>();
itemList.add(
new Column(
children: <Widget>[
new RaisedButton(
onPressed: ()=>
Navigator.of(context).push(new MaterialPageRoute(builder: (_)=>new PageHomeContent()),)
.then((val)=>{ getRefreshRequests() }),
child: Text('Refresh', style: TextStyle(color: Colors.white), ), color: Colors.purple,
elevation: 2.0,
),
],
)
);
itemList.add(
getPagebuilderList(isShowTitle: false)
);
itemList.add(
featuredPostBuildSlider(_futureFeaturedArticles)
);
/*itemList.add(
featuredPost(_futureFeaturedArticles),
);*/
itemList.add(
widgetCategoryBuilder
);
itemList.add(
latestPosts(_futureLastestArticles)
);
return itemList;
}
_scrollListener() {
var isEnd = _controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange;
if (isEnd) {
setState(() {
page += 1;
_futureLastestArticles = fetchLatestArticles(page);
});
}
}
//region [ ALL POST | RECENTS POST ]
//endregion
//region [ POST FEATURED | Swiper ]
//endregion
//region [ PAGES ]
//endregion
//region [ CATEGORIES LOCAL --> ON LIGNE ]
void getCategoriesOnLocal() async {
try {
await _categoryRepository.getCategories().then((itemList) {
if (itemList != null) {
setState(() {
categoryHomeList = itemList;
});
getCategoryBuilder();
}
});
} catch (e) {
Tools.println("Error: getCategoriesOnLocal: $e");
}
}
getCategoryBuilder() {
List<Widget> itemWidgetList=[];
if( _showLoadingCategoryHome) {
if (categoryHomeList.length > 0) {
for (Category category in categoryHomeList) {
if (category.count > 0) {
itemWidgetList.add(
getItemArticle(category: category)
);
}
}
widgetCategoryBuilder= Column( children: itemWidgetList );
} else {
widgetCategoryBuilder= Container();
}
} else {
widgetCategoryBuilder= Container();
}
setState(() {
widgetCategoryBuilder = widgetCategoryBuilder;
});
return widgetCategoryBuilder;
}
Widget getItemArticle({Category category}) {
return
Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 8.0, right: 8.0),
child: Row(
children: <Widget>[
Text('${category.name}',
style: homeTitleTextStyle,
textAlign: TextAlign.left,),
Spacer(),
InkWell(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CategoryArticles(category.id, category.name),
),
);
},
child: Text('See More',
textAlign: TextAlign.right,
style: TextStyle(color: Colors.red),),
),
],),
),
new CategoryHomeBuilder( categorieId: category.id),
],
);
}
//endregion
}
Does anyone have a suggestion.
Thanks for your help
Waiting for a better response.
I replaced the BuildDrawer class with a getBuildDrawer() method in the main class.
And it works very well but I would have preferred to put it in a separate class, so that I can use it in another page ...
getBuildDrawer() {
return ClipPath(
clipper: OvalRightBorderClipper(),
child: Drawer(
child: Container(
padding: const EdgeInsets.only(left: 16.0, right: 40),
decoration: BoxDecoration(
color: primaryColor,
boxShadow: [BoxShadow(color: Colors.black45)]),
width: 300,
child: SafeArea(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: InkWell(
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(new MaterialPageRoute(builder: (_)=>new PageHomeContent()),)
.then((val)=>{ getRefreshRequests() });
},
child:
Column(
children: <Widget>[
Row(
children: [
Icon(
Icons.format_list_bulleted,
color: activeColor,
),
SizedBox(width: 10.0),
Text("Home Content", ),
Spacer(),
]
),
],
),
),
),
Divider(
color: dividerColor,
),
],
),
),
),
),
),
);
}
you have to refresh the page just putting setState(getRefreshRequests()) when you return from navigator, that's because the page don't know that you put a new widget on screen
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,
),
],
),
),