So, I'm trying to display an Item on my app using the bloc pattern but I'm getting this error:
The following LateError was thrown building ItemDetailsScreen(state: _ItemDetailsScreenState#55c51):
LateInitializationError: Field 'item' has not been initialized.
The relevant error-causing widget was:
ItemDetailsScreen
ItemDetailsScreen:file:/mobile/lib/src/autoroute/app_router.gr.dart:70:40
Below the app_router.dart
AutoRoute(
path: '/item/:itemId',
page: ItemDetailsScreen,
name: 'ItemDetailsRoute',
meta: {'hideBottomNav': true},
guards: [AuthGuard],
),
and the app_router.gr.dart
ItemDetailsRoute.name: (routeData) {
final pathParams = routeData.inheritedPathParams;
final args = routeData.argsAs<ItemDetailsRouteArgs>(
orElse: () =>
ItemDetailsRouteArgs(itemId: pathParams.getInt('itemId')));
return MaterialPageX<dynamic>(
routeData: routeData, child: ItemDetailsScreen(args.itemId));
},
And view code in itemDetails.dart file as bellow
class ItemDetailsScreen extends StatefulWidget {
final int itemId;
const ItemDetailsScreen(#pathParam this.itemId, {Key? key}) : super(key: key);
#override
_ItemDetailsScreenState createState() => _ItemDetailsScreenState();
}
class _ItemDetailsScreenState extends State<ItemDetailsScreen> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
//final auth = Provider.of<KeycloakAuth>(context);
return BlocBuilder<ItemBloc, ItemState>(builder: (context, state) {
if (state is ItemLoadingState) {
return const MyCircularProgressIndicator();
}
if (state is ItemLoadedState) {
Item showItem = state.item;
return Scaffold(
appBar: AppBar(
title: Text(showItem.name),
),
body: ListView(
children: [
CarouselSlider(
options: CarouselOptions(
height: 300.0,
enableInfiniteScroll: false,
aspectRatio: 16 / 10,
viewportFraction: 1.0,
),
items: showItem.pictures.whereNotNull().map((String e) {
return CachedNetworkImage(
imageUrl: "${e}_SMALL.jpg",
placeholder: (context, url) =>
const CircularProgressIndicator(),
errorWidget: (context, url, error) =>
const Icon(Icons.error),
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
);
}).toList()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Column(
children: [
const SizedBox(
height: 15,
),
Text(
showItem.name,
style: MyTheme.bigBlack,
),
const SizedBox(
height: 15,
),
],
),
)
],
),
);
}
if (state is ItemLoadingErrorState) {
return Center(
child: Text(state.error.toString()),
);
}
return Container();
});
}
}
Any idea how I can fix it ? thank you in advance for your answers.
in file itemDetails.dart you declared final int itemId; inside class ItemDetailsScreen
when you called ItemDetailsScreen in AutoRoute you did not pass a value for final int itemId;
When you write your code in null safety you should declare something to the variable's .So declare some values to final int itemid
Related
I am trying to implement algolia search in flutter with filters.
I found an article on the algolia website which I followed to a tee to get to this stage in the implementation but I am getting this error and have no clue what to do about it:
lib/services/search.dart:194:52: Error: Member not found: 'fromResponse'.
_assetsSearcher.responses.map(SearchMetadata.fromResponse);
^^^^^^^^^^^^
lib/services/search.dart:200:46: Error: Member not found: 'fromResponse'.
_assetsSearcher.responses.map(HitsPage.fromResponse);
^^^^^^^^^^^^
This is my code:
class SearchMetadata {
final int nbHits;
const SearchMetadata(this.nbHits);
factory SearchMetadata.fromResponse(SearchResponse response) =>
SearchMetadata(response.nbHits);
}
class Asset {
final String code;
final String name;
final String desc;
final String loc;
final String img;
Asset(this.code, this.name, this.desc, this.loc, this.img);
static Asset fromJson(Map<String, dynamic> json) {
return Asset(
json['name'], json['desc'], json['desc'], json['loc'], json['img']);
}
}
class HitsPage {
const HitsPage(this.assets, this.pageKey, this.nextPageKey);
final List<Asset> assets;
final int pageKey;
final int? nextPageKey;
factory HitsPage.fromResponse(SearchResponse response) {
final assets = response.hits.map(Asset.fromJson).toList();
final isLastPage = response.page >= response.nbPages;
final nextPageKey = isLastPage ? null : response.page + 1;
return HitsPage(assets, response.page, nextPageKey);
}
}
class AlgoliaSearchFilters extends StatefulWidget {
const AlgoliaSearchFilters({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<AlgoliaSearchFilters> createState() => _AlgoliaSearchFiltersState();
}
class _AlgoliaSearchFiltersState extends State<AlgoliaSearchFilters> {
final _searchTextController = TextEditingController();
final _assetsSearcher = HitsSearcher(
applicationID: 'OPN6AROJK6',
apiKey: '0ba458475d25b7e2069d700c32e42f29',
indexName: 'asset');
Stream<SearchMetadata> get _searchMetadata =>
_assetsSearcher.responses.map(SearchMetadata.fromResponse);
final PagingController<int, Asset> _pagingController =
PagingController(firstPageKey: 0);
Stream<HitsPage> get _searchPage =>
_assetsSearcher.responses.map(HitsPage.fromResponse);
final GlobalKey<ScaffoldState> _mainScaffoldKey = GlobalKey();
final _filterState = FilterState();
late final _facetList = FacetList(
searcher: _assetsSearcher, filterState: _filterState, attribute: 'loc');
#override
void initState() {
super.initState();
_searchTextController
.addListener(() => _assetsSearcher.query(_searchTextController.text));
_searchPage.listen((page) {
if (page.pageKey == 0) {
_pagingController.refresh();
}
_pagingController.appendPage(page.assets, page.nextPageKey);
}).onError((error) => _pagingController.error = error);
_pagingController.addPageRequestListener((pageKey) =>
_assetsSearcher.applyState((state) => state.copyWith(page: pageKey)));
_assetsSearcher.connectFilterState(_filterState);
_filterState.filters.listen((_) => _pagingController.refresh());
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _mainScaffoldKey,
appBar: AppBar(
backgroundColor: Colors.blue[900],
title: const Text("Algolia Flutter Search"),
actions: [
IconButton(
onPressed: () => _mainScaffoldKey.currentState?.openEndDrawer(),
icon: const Icon(Icons.filter_list_sharp))
],
),
endDrawer: Drawer(
child: _filters(context),
),
body: Center(
child: Column(
children: <Widget>[
SizedBox(
height: 44,
child: TextField(
controller: _searchTextController,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term',
prefixIcon: Icon(Icons.search),
),
)),
StreamBuilder<SearchMetadata>(
stream: _searchMetadata,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text('${snapshot.data!.nbHits} hits'),
);
},
),
Expanded(
child: _hits(context),
)
],
),
),
);
}
Widget _hits(BuildContext context) => PagedListView<int, Asset>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<Asset>(
noItemsFoundIndicatorBuilder: (_) => const Center(
child: Text('No results found'),
),
itemBuilder: (_, item, __) => Container(
color: Colors.white,
height: 80,
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
SizedBox(width: 50, child: Image.network(item.img)),
const SizedBox(width: 20),
Expanded(child: Text(item.name))
],
),
)));
Widget _filters(BuildContext context) => Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue[900],
title: const Text('Filters'),
),
body: StreamBuilder<List<SelectableItem<Facet>>>(
stream: _facetList.facets,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
}
final selectableFacets = snapshot.data!;
return ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: selectableFacets.length,
itemBuilder: (_, index) {
final selectableFacet = selectableFacets[index];
return CheckboxListTile(
value: selectableFacet.isSelected,
title: Text(
'${selectableFacet.item.value} (${selectableFacet.item.count})'),
onChanged: (_) {
_facetList.toggle(selectableFacet.item.value);
},
);
});
}),
);
#override
void dispose() {
_searchTextController.dispose();
_assetsSearcher.dispose();
_pagingController.dispose();
_filterState.dispose();
_facetList.dispose();
super.dispose();
}
}
It shows up as an error only when I run my program
Please let me know of any fixes
Thanks!
I had the same issue after updating flutter. Try run
flutter pub upgrade
to upgrade all your dependencies as this is caused by a transitive dependencies.
I am trying to learn MVVM.
But I'm having a problem with the "StreamBuilder" ,
when i run app then edit something then press Ctrl+s (save File) thats error show up:
The following StateError was thrown building OnBoardingView(state: _OnBoardingViewState#f7e20):
Bad state: Stream has already been listened to.
The relevant error-causing widget was
OnBoardingView
lib\…\0-resources\routes_manager.dart:38
When the exception was thrown, this was the stack
my code =>
there are two files
1- view Model:
import 'package:bak/Presentation/0-Resources/color_manager.dart';
import 'package:bak/Presentation/2-OnBoarding/viewmodel/onboarding_viewmodel.dart';
import 'package:flutter/material.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class OnBoardingView extends StatefulWidget {
const OnBoardingView({super.key});
#override
State<OnBoardingView> createState() => _OnBoardingViewState();
}
class _OnBoardingViewState extends State<OnBoardingView> {
final OnBoardingViewModel _viewModel = OnBoardingViewModel();
final PageController _pageController = PageController();
_bind() {
_viewModel.start();
}
#override
void initState() {
_bind();
super.initState();
}
#override
Widget build(BuildContext context) {
return StreamBuilder<OnBoardingViewObject>(
stream: _viewModel.outputOnBoardingViewObject.asBroadcastStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Scaffold(
backgroundColor: ColorManager.darkPrimary,
body: SafeArea(
child: Column(children: [
////////////////////
/* Page */
Expanded(
child: SizedBox(
child: PageView.builder(
onPageChanged: (i) {
if (i == snapshot.data!.pagesData.length - 1) {
_viewModel.skip();
}
},
physics: const BouncingScrollPhysics(),
controller: _pageController,
itemCount: snapshot.data!.pagesData.length,
itemBuilder: (context, index) => OnBordingPage(
imagePath: snapshot.data!.pagesData[index].imagePath,
title: snapshot.data!.pagesData[index].title,
subTitle: snapshot.data!.pagesData[index].subTitle),
),
),
),
////////////////////
/* Circles */
Container(
alignment: Alignment.center,
height: 20,
width: double.infinity,
child: SmoothPageIndicator(
effect: SwapEffect(
dotColor: ColorManager.primary,
activeDotColor: ColorManager.logo),
controller: _pageController,
count: snapshot.data!.pagesData.length <= 1
? 2
: snapshot.data!.pagesData.length)),
////////////////////
/* Button */
Container(
alignment: Alignment.center,
height: 100,
width: double.infinity,
child: ElevatedButton(
onPressed: () {
_viewModel.skip();
_pageController.animateToPage(
snapshot.data!.pagesData.length - 1,
duration: const Duration(microseconds: 300),
curve: Curves.easeIn);
},
child: Text(snapshot.data!.buttonText),
)),
]),
),
);
} else {
return const Text('sedf');
}
},
);
}
#override
void dispose() {
_viewModel.dispose();
super.dispose();
}
}
class OnBordingPage extends StatelessWidget {
const OnBordingPage(
{super.key,
required this.imagePath,
required this.title,
required this.subTitle});
final String imagePath;
final String title;
final String subTitle;
#override
Widget build(BuildContext context) {
return Column(
children: [
////////////////////
/* Icon */
Container(
margin:
const EdgeInsets.only(left: 50, right: 50, top: 100, bottom: 50),
child: Image.asset(
imagePath,
),
),
////////////////////
/* Title */
Container(
margin: const EdgeInsets.symmetric(horizontal: 50, vertical: 25),
child: AutoSizeText(
title,
style: Theme.of(context).textTheme.headlineLarge,
)),
////////////////////
/* subTitle */
Container(
margin: const EdgeInsets.symmetric(
horizontal: 50,
),
child: AutoSizeText(
subTitle,
style: Theme.of(context).textTheme.titleMedium,
)),
],
);
}
}
second file:
// ignore_for_file: unused_field, prefer_final_fields
import 'dart:async';
import 'package:bak/Presentation/0-Base/baseviewmodel.dart';
import '../../../Domain/models.dart';
import '../../0-Resources/assets_manager.dart';
class OnBoardingViewModel extends BaseViewModel
with OnBoardingViewModelInputs, OnBoardingViewModelOutputs {
StreamController _streamController = StreamController<OnBoardingViewObject>();
List<OnBoardingPageObject> pagesDate = [
OnBoardingPageObject(
imagePath: ImageAssetsManager.transparentLogo,
title: 'بكلوريتي',
subTitle: 'subTitle'),
OnBoardingPageObject(
imagePath: ImageAssetsManager.idea,
title: 'أختبر نفسك',
subTitle: 'subTitle'),
OnBoardingPageObject(
imagePath: ImageAssetsManager.test,
title: 'العنوان',
subTitle: 'subTitle'),
OnBoardingPageObject(
imagePath: ImageAssetsManager.endTest,
title: 'أختبر نفسك',
subTitle: 'subTitle'),
OnBoardingPageObject(
imagePath: ImageAssetsManager.globe,
title: 'العنوان',
subTitle: 'subTitle'),
OnBoardingPageObject(
imagePath: ImageAssetsManager.onlineLearning,
title: 'العنوان',
subTitle: 'subTitle'),
OnBoardingPageObject(
imagePath: ImageAssetsManager.read,
title: 'العنوان',
subTitle: 'subTitle'),
];
// OnBoarding ViewModel Inputs
#override
void dispose() {
_streamController.close();
}
#override
void start() {
pagesDate;
postData();
}
#override
void skip() {
inputOnBoardingViewObject.add(OnBoardingViewObject(
currentPage: pagesDate.length - 1,
buttonText: 'البدئ',
pagesData: pagesDate,
));
}
#override
void finish() {
// TODO: implement finish
}
#override
Sink get inputOnBoardingViewObject => _streamController.sink;
#override
Stream<OnBoardingViewObject> get outputOnBoardingViewObject =>
_streamController.stream
.map((onBoardingViewObject) => onBoardingViewObject);
// Data
void postData() {
inputOnBoardingViewObject.add(OnBoardingViewObject(
currentPage: 0,
buttonText: 'تخطي',
pagesData: pagesDate,
));
}
}
// inputs means that "Orders" that our view model will receive
abstract class OnBoardingViewModelInputs {
//functions here
void skip();
void finish();
//stream controller inputs here
Sink get inputOnBoardingViewObject;
}
abstract class OnBoardingViewModelOutputs {
//stream controller inputs here
Stream<OnBoardingViewObject> get outputOnBoardingViewObject;
}
// class that handle all data that comes from viewmodel to view
class OnBoardingViewObject {
int currentPage;
String buttonText;
List<OnBoardingPageObject> pagesData;
OnBoardingViewObject({
required this.currentPage,
required this.buttonText,
required this.pagesData,
});
}
i have tried two method so solve this but non of them worked..
first one:(that one make snapeshot null)
StreamController _streamController =
StreamController<OnBoardingViewObject>.broadcast();
second one: (nothing change here)
stream: _viewModel.outputOnBoardingViewObject.asBroadcastStream(),
Generated list is sending null future
class _HomePageState extends State<HomePage> {
AppStateViewModal appStateViewModal = AppStateViewModal();
late Future<List<PackageDataModal>> packageList;
#override
void initState() {
super.initState();
packageList = _getPackageList();
}
Future<List<PackageDataModal>> _getPackageList() async {
return await appStateViewModal.getPackages();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: packageList,
builder: (context,
AsyncSnapshot<List<PackageDataModal>> packageSnapshot) =>
packageSnapshot.hasData
? GridView.builder(
itemCount: packageSnapshot.data!.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1),
itemBuilder: (context, index) => CardView(
packageData: PackageDataModal(
title: packageSnapshot.data![index].title,
description: packageSnapshot.data![index].description,
packageId: packageSnapshot.data![index].packageId),
imageList: const <ImageDataModal>[
// Generate a "Imagelist" for every "packageId"
// packageId is "$index"
// I want to return "List<ImageDataModal>""
// But I am getting "Future<List<ImageDataModal>>""
// to fix it, i just need to "await" but build cant do "Async"
// What i am doing wrong ?
// ApiUrl is like /api/packages
// and after this
// ApiUrl is like /api/packages/$index/images as List
// BackEnd Services is sending back the data to widget
// I have seen with doing print statments
// First FutureBuilder is running fine but when it comes to next
// Data is not showing.
// Dummy ImageDataModal is working and shwoing the expected behaviour
],
),
)
: const Center(
child: CircularProgressIndicator(),
),
);
}
}
I was trying to Pass the Future to next Widget and then trying to use FutureBuilder but no success. Data is not reaching there.
class CardView extends StatefulWidget {
const CardView({Key? key, required this.imageList, required this.packageData})
: super(key: key);
final PackageDataModal packageData;
final Future<List<ImageDataModal>> imageList;
#override
State<CardView> createState() => _CardViewState();
}
class _CardViewState extends State<CardView> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<ImageDataModal>>(
future: widget.imageList,
builder: (context, imageSnapshot) => Card(
margin: const EdgeInsets.all(8.0),
child: Column(
children: [
CarouselSlider.builder(
itemCount: imageSnapshot.data!.length,
itemBuilder: (context, imageIndex, pageIndex) =>
CachedNetworkImage(
imageUrl: imageSnapshot.data![imageIndex].image,
placeholder: (context, text) => const Placeholder(),
),
options:
CarouselOptions(aspectRatio: 16 / 9, autoPlay: true),
),
Text(widget.packageData.title),
const SizedBox(height: 2),
Text(widget.packageData.description),
],
),
));
}
}
I checked the Service which is calling the api to fetch the data is working fine and i checked it that it is reaching the card_view_widget.dart file properly
First correct your card_view_widget to be null aware(Handle null list)
class CardView extends StatefulWidget {
const CardView({Key? key, this.imageList, required this.packageData})
: super(key: key);
final PackageDataModal packageData;
final Future<List<ImageDataModal>>? imageList;
#override
State<CardView> createState() => _CardViewState();
}
class _CardViewState extends State<CardView> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<ImageDataModal>>(
future: widget.imageList,
builder: (context, imageSnapshot) {
if (!imageSnapshot.hasData &&
imageSnapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (!imageSnapshot.hasData) {
return Text(
'No image found',
style: Theme.of(context).textTheme.subtitle1,
);
}
return Card(
margin: const EdgeInsets.all(8.0),
child: Column(
children: [
CarouselSlider.builder(
itemCount: imageSnapshot.data!.length,
itemBuilder: (context, imageIndex, pageIndex) =>
CachedNetworkImage(
imageUrl: imageSnapshot.data![imageIndex].image,
placeholder: (context, text) => const Placeholder(),
),
options: CarouselOptions(aspectRatio: 16 / 9, autoPlay: true),
),
Text(widget.packageData.title),
const SizedBox(height: 2),
Text(widget.packageData.description),
],
),
);
},
);
}
}
let me know of other errors
I found the solution. My data model, which is Model.fromjson, was mapping the wrong key. I was getting the HTTP response properly, but it was sending the null data to the Future<List>.
It was String itemId, but I changed it previously and forgot to do the fix to String packageId as I was getting the JSON string.
How can I send the data I get from the API to the other pages? Before using getx i was sending with "widget.bla bla" but now i don't know how can i send it.
class HomePage extends StatelessWidget {
final AllCoinController allCoinController = Get.put(AllCoinController());
#override
Widget build(BuildContext context) {
return Scaffold(
body: Obx(
() => ListView.builder(
scrollDirection: Axis.vertical,
itemCount: allCoinController.coinList.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
Get.to(CoinContent());
},
child: Container(
color: Colors.grey[700],
width: 150,
child: Row(
children: [
SizedBox(
width: 50,
height: 50,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
allCoinController.coinList[index].image),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(allCoinController.coinList[index].name),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(allCoinController.coinList[index].symbol),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(allCoinController
.coinList[index].currentPrice
.toString()),
),
],
),
),
),
);
},
),
),
);
}
}
The page I want to send the data to:
class CoinContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("coin name"),
),
body: Obx(
() => Center(
child: Column(
children: [
Text("coin data 1"),
Text("coin data 2"),
Text("coin data 3"),
Text("coin data 4"),
Text("coin data 5"),
Text("coin data 6"),
],
),
),
),
);
}
}
And my last question codes are not found automatically when using Getx. Example:
Text(allCoinController.coinList[index].currentPrice.toString()),
I get the same data without using getx and there was no problem. But when using Getx the "currentPrice" code is not automatically found and does not appear. I need to copy the code to write.
My controller:
import 'dart:async';
import 'package:coin_finder/models/btc_eth_bnb_model.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
class AllCoinController extends GetxController {
var coinList = [].obs;
final url = Uri.parse("api url")
Future callAllCoins() async {
try {
final response = await http.get(url);
if (response.statusCode == 200) {
List<dynamic> values = [];
values = allCoinsFromJson(response.body);
coinList.assignAll(values);
if (values.length > 0) {
for (int i = 0; i < values.length; i++) {
if (values[i] != null) {
coinList.add(values[i]);
}
}
}
return coinList;
} else {
print(response.statusCode);
}
} catch (e) {
print(e.toString());
}
}
#override
void onInit() {
callAllCoins();
Timer.periodic(Duration(minutes: 5), (timer) => callAllCoins());
super.onInit();
}
}
Model:
import 'dart:convert';
List<AllCoins> allCoinsFromJson(String str) =>
List<AllCoins>.from(json.decode(str).map((x) => AllCoins.fromJson(x)));
String allCoinsToJson(List<AllCoins> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class AllCoins {
AllCoins({
required this.symbol,
required this.name,
required this.image,
required this.currentPrice,
});
String symbol;
String name;
String image;
num currentPrice;
factory AllCoins.fromJson(Map<String, dynamic> json) => AllCoins(
symbol: json["symbol"],
name: json["name"],
image: json["image"],
currentPrice: json["current_price"],
);
Map<String, dynamic> toJson() => {
"symbol": symbol,
"name": name,
"image": image,
"current_price": currentPrice,
};
}
Dart version: sdk: ">=2.12.0 <3.0.0"
in home page Get.to(CoinContent(),arguments:
allCoinController.coinList[index] )
//
class CoinContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context)!.settings.arguments as
AllCoins;
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("coin name"),
),
body:Center(
child: Column(
children: [
Text("${data.name}"),
],
),
),
);
}
}
in this case we aren't using agrs in constructor
instead of routing like that Get.to(MyScreen());
you should use Get.to(MyScreen(), arguments: [1,2,3]);
you can access them by using Get.arguments it should return a List
Note: The arguments will not change while using the app until you override it by routing to another screen with args
I'm new to Flutter Redux, I got a problem and I have no idea how to deal with it at all! I extracted the main code to keep this simple - tap indicators to switch PageView, scroll PageView to synchronise the indicator. Here is my code:
app state:
class AppState {
final List menuList;
final int currentIndex;
AppState({this.menuList, this.currentIndex});
}
the reducers:
AppState appReducer(AppState state, Object action) {
return AppState(
menuList: menuListReducer(state.menuList, action),
currentIndex: currentIndexReducer(state.currentIndex, action));
}
final menuListReducer = combineReducers<List>(
[TypedReducer<List, SetMenuListAction>(_setMenuList)]);
List _setMenuList(List menuList, SetMenuListAction action) {
menuList = action.menuList;
return menuList;
}
final currentIndexReducer = combineReducers<int>(
[TypedReducer<int, SetCurrentIndexAction>(_setCurrentIndex)]);
int _setCurrentIndex(int currentIndex, SetCurrentIndexAction action) {
currentIndex = action.index;
return currentIndex;
}
the action:
class SetMenuListAction {
List menuList;
SetMenuListAction(this.menuList);
}
class SetCurrentIndexAction {
int index;
SetCurrentIndexAction(this.index);
}
the main logic:
void main() {
final store = Store<AppState>(
appReducer,
initialState: AppState(menuList: [
{
'picUrl': 'http://pic3.16pic.com/00/55/42/16pic_5542988_b.jpg',
'description': 'this is the first image'
},
{
'picUrl': 'http://photo.16pic.com/00/38/88/16pic_3888084_b.jpg',
'description': 'this is the second image'
},
{
'picUrl':
'http://img4.imgtn.bdimg.com/it/u=3434394339,2114652299&fm=214&gp=0.jpg',
'description': 'this is the third image'
},
{
'picUrl': 'http://pic1.win4000.com/pic/2/07/8c57e143b1.jpg',
'description': 'this is the fourth image'
},
], currentIndex: 0),
);
runApp(App(
store: store,
));
}
// App
class App extends StatelessWidget {
final Store<AppState> store;
const App({Key key, this.store}) : super(key: key);
#override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: MaterialApp(title: 'Flutter redux example', home: MyDetail()),
);
}
}
class MyDetail extends StatefulWidget {
#override
_MyDetailState createState() => _MyDetailState();
}
class _MyDetailState extends State<MyDetail> with TickerProviderStateMixin {
PageController _controller;
#override
void initState() {
_controller = PageController(initialPage: 0);
super.initState();
}
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, int>(
converter: (store) => store.state.currentIndex,
onDidChange: (newIdx) {
//this won't work because the _controller hasn't been attached to PageView
_controller.jumpToPage(newIdx);
},
builder: (BuildContext context, int idx) {
return StoreConnector<AppState, List>(
converter: (store) => store.state.menuList,
onDidChange: (newList) {
//maybe do something further
},
builder: (BuildContext context, List menus) {
return Container(
color: Colors.white,
child: Column(
children: <Widget>[
//pageview
Expanded(
child: PageView(
children: menus.map((item) {
return Column(
children: <Widget>[
Image.network(item['picUrl']),
Text(
item['description'],
style: TextStyle(fontSize: 24.0),
)
],
);
}).toList(),
onPageChanged: (int index) {
StoreProvider.of<AppState>(context)
.dispatch(SetCurrentIndexAction(index));
},
physics: BouncingScrollPhysics(),
),
),
//indicators
Container(
margin: EdgeInsets.only(bottom: 50.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: menus
.asMap()
.map((i, item) => MapEntry(
i,
GestureDetector(
onTap: () {
//this won't work either maybe because the widgets is rebuilding
_controller.jumpToPage(i);
StoreProvider.of<AppState>(context)
.dispatch(SetCurrentIndexAction(i));
},
child: Container(
width: 10.0,
height: 10.0,
color: i == idx
? Colors.purpleAccent
: Colors.blue,
margin: EdgeInsets.only(right: 10.0),
),
)))
.values
.toList(),
),
)
],
),
);
},
);
},
);
}
}
Sorry for the long code, but I think maybe this can help to understand my problem:
When I tap the indicator, I want to synchronise the PageView, that is _controller.jumpToPage(i), but it will show Errors. So how to make this work?
I can change the currentIndex in another screen, how to synchronise the PageView?
Is there any method to watch the state changes(separately, not the whole state) and do something?
After debugging your code I found that you are missing controller: _controller in PageView, this should fix it:
Expanded(
child: PageView(
controller: _controller,
children: menus.map((item) {
return Column(
children: <Widget>[
Image.network(item['picUrl']),
Text(
item['description'],
style: TextStyle(fontSize: 24.0),
)
],
);
}).toList(),
onPageChanged: (int index) {
StoreProvider.of<AppState>(context)
.dispatch(SetCurrentIndexAction(index));
},
physics: BouncingScrollPhysics(),
),
),