login page in bottomNavigationBar with flutter bloc - flutter

need help.. I am new flutter bloc. I am trying implement login page in bottom navigation bar with bloc but not success. the navbar is:
beranda
pelatihan
pengetahuan
bantuan
profil (in profil if AuthenticationStatus.authenticated route to home page, if AuthenticationStatus.unauthenticated route to login page).
following code navigation cubit from https://medium.com/#antoniodominikovic/implementing-in-app-navigation-with-the-flutter-bottom-navigation-bar-using-the-bloc-architecture-84cda9c74dda and login bloc from https://bloclibrary.dev/#/flutterlogintutorial.
main class
void main() {
BlocOverrides.runZoned(
() async{
// Use blocs...
WidgetsFlutterBinding.ensureInitialized();
final userRepository = UserRepository();
userRepository.user?.id;
runApp(App(
authenticationRepository: AuthenticationRepository(),
userRepository: UserRepository(), connectivity: Connectivity(),
));
},
blocObserver: AppBlocObserver(),
);
}
app class:
class App extends StatelessWidget {
const App({
Key? key,
required this.authenticationRepository,
required this.userRepository,
required this.connectivity,
}) : super(key: key);
final AuthenticationRepository authenticationRepository;
final UserRepository userRepository;
final Connectivity connectivity;
#override
Widget build(BuildContext context) {
return RepositoryProvider.value(
value: authenticationRepository,
child: MultiBlocProvider(
providers: [
BlocProvider<NavigationCubit>(
create: (context) => NavigationCubit(),
),
BlocProvider<InternetCubit>(
create: (context) => InternetCubit(connectivity: connectivity)),
BlocProvider<AuthenticationBloc>(
create: (context) => AuthenticationBloc(
authenticationRepository: authenticationRepository,
userRepository: userRepository,
),
)
],
child: AppView(),
),
);
}
}
appview class:
class AppView extends StatefulWidget {
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
final _navigatorKey = GlobalKey<NavigatorState>();
NavigatorState get _navigator => _navigatorKey.currentState!;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.white,
bottomNavigationBar: BlocBuilder<NavigationCubit, NavigationInitial>(
builder: (context, state) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 20,
color: Colors.black.withOpacity(.1),
)
],
),
child: SafeArea(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8),
child: GNav(
rippleColor: Colors.grey[300]!,
hoverColor: Colors.grey[100]!,
gap: 8,
activeColor: Colors.black,
iconSize: 24,
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 12),
duration: const Duration(milliseconds: 400),
tabBackgroundColor: Colors.grey[100]!,
color: Colors.black,
tabs: const [
GButton(
icon: LineIcons.home,
text: 'Home',
),
GButton(
icon: LineIcons.book,
text: 'Pelatihan',
),
GButton(
icon: LineIcons.search,
text: 'Pengetahuan',
),
GButton(
icon: LineIcons.facebookMessenger,
text: 'Bantuan',
),
GButton(
icon: LineIcons.user,
text: 'Profil',
),
],
selectedIndex: state.index,
onTabChange: (index) {
setState(() {
if (index == 0) {
BlocProvider.of<NavigationCubit>(context)
.getNavBarItem(NavbarItem.beranda);
} else if (index == 1) {
BlocProvider.of<NavigationCubit>(context)
.getNavBarItem(NavbarItem.pelatihan);
} else if (index == 2) {
BlocProvider.of<NavigationCubit>(context)
.getNavBarItem(NavbarItem.pengetahuan);
} else if (index == 3) {
BlocProvider.of<NavigationCubit>(context)
.getNavBarItem(NavbarItem.bantuan);
} else if (index == 4) {
BlocProvider.of<NavigationCubit>(context)
.getNavBarItem(NavbarItem.profil);
}
});
},
),
),
),
);
},
),
body: BlocBuilder<NavigationCubit, NavigationInitial>(
builder: (context, statenav) {
if (statenav.navbarItem == NavbarItem.beranda) {
return const BerandaPage();
} else if (statenav.navbarItem == NavbarItem.pelatihan) {
return const PelatihanPage();
} else if (statenav.navbarItem == NavbarItem.pengetahuan) {
return const PengetahuanPage();
} else if (statenav.navbarItem == NavbarItem.bantuan) {
return const BantuanPage();
} else if (statenav.navbarItem == NavbarItem.profil) {
// return LoginPage();
return MaterialApp(
navigatorKey: _navigatorKey,
builder: (context, child) {
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
switch (state.status) {
case AuthenticationStatus.authenticated:
_navigator.pushAndRemoveUntil<void>(
HomePage.route(),
(route) => false,
);
statenav.navbarItem == NavbarItem.profil;
break;
case AuthenticationStatus.unauthenticated:
_navigator.pushAndRemoveUntil<void>(
LoginPage.route(),
(route) => false,
);
statenav.navbarItem == NavbarItem.profil;
break;
default:
break;
}
},
child: child,
);
},
onGenerateRoute: (_) => SplashPage.route(),
);
}
return Container();
}),
),
);
}
}
there is no error code, when the first time I select the profile tab, the login page appears. But when I select another tab and go back to the profile tab, CircularProgressIndicator from the splash page is appear. please tell me and teach me or any references?, thanks

Related

Error: Could not find the correct Provider<RecipeProvider> above this AddRecipe Widget

`I want to access the provider in the AddRecipe page in order to save a new recipe and notify the listeners, in my case the list.builder in UserRecipes to rebuild.
Sorry for the big amount of code, I added everything that I thought may be useful. I just can't figure it out, have been trying for hours.
This is the error that I get:
ProviderNotFoundException (Error: Could not find the correct Provider<RecipeProvider> above this AddRecipe Widget
These are the files:
Wrapper.dart:
class Wrapper extends StatelessWidget {
const Wrapper({super.key});
#override
Widget build(BuildContext context) {
final user = Provider.of<User?>(context);
if (user == null) {
return const AuthPage();
} else {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => RecipeProvider(uid: user.uid))
],
child: const HomePage()
);
}
}
}
Home.dart:
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final AuthService _auth = AuthService();
// The 4 main application screens
static const List<Destination> allDestinations = <Destination>[
Destination(0, 'Home', Icons.home, Colors.teal),
Destination(1, 'Meals', Icons.dinner_dining, Colors.orange),
Destination(2, 'Recipes', Icons.restaurant_menu_sharp, Colors.amber),
Destination(3, 'Profile', Icons.person, Colors.blue),
];
int currentPageIndex = 0;
final screens = [
Center(child: Text('Home'),),
Center(child: Text('Meals'),),
RecipesPage(),
Center(child: Text('My profile'),),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: allDestinations[currentPageIndex].title, backButton: false, signOutButton: true),
bottomNavigationBar: NavigationBar(
onDestinationSelected: (int index) {
setState(() {
currentPageIndex = index;
});
},
selectedIndex: currentPageIndex,
destinations: allDestinations.map((Destination destination) {
return NavigationDestination(
icon: Icon(destination.icon, color: destination.color),
label: destination.title
);
}).toList(),
),
body: screens[currentPageIndex]
);
}
}
class Destination {
const Destination(this.index, this.title, this.icon, this.color);
final int index;
final String title;
final IconData icon;
final MaterialColor color;
}
Recipes.dart:
const List<Widget> recipes = <Widget>[
Text('My recipes'),
Text('Other recipes')
];
class RecipesPage extends StatefulWidget {
const RecipesPage({super.key});
#override
State<RecipesPage> createState() => _RecipesPageState();
}
class _RecipesPageState extends State<RecipesPage> {
final List<bool> _selectedRecipes = <bool>[true, false];
final _searchContoller = TextEditingController();
#override
void dispose() {
_searchContoller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var provider = context.watch<RecipeProvider>();
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 20),
ToggleButtons(
direction: Axis.horizontal,
onPressed: (int index) {
setState(() {
for (int i = 0; i < _selectedRecipes.length; i++) {
_selectedRecipes[i] = i == index;
}
});
},
borderRadius: const BorderRadius.all(Radius.circular(12)),
selectedBorderColor: Colors.blue[700],
selectedColor: Colors.white,
fillColor: Colors.blue[200],
color: Colors.blue[400],
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 165.0,
),
isSelected: _selectedRecipes,
children: recipes
),
SizedBox(height: 10),
// Search textfield
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: TextField(
controller: _searchContoller,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white),
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(12),
),
hintText: 'Search',
fillColor: Colors.grey[250],
filled: true
),
),
),
SizedBox(height: 20),
Expanded(
child: _getRecipePage(),
),
],
)
),
),
floatingActionButton: Consumer<RecipeProvider>(
builder: (context, value, child) {
return _getFAB();
},
)
);
}
Widget _getFAB() {
if (_selectedRecipes[1]) {
return Container();
} else {
return FloatingActionButton(
child: Icon(Icons.add, size: 35),
onPressed: () => {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => AddRecipe()
)
),
},
);
}
}
Widget _getRecipePage() {
if (_selectedRecipes[0]) {
return UserRecipesWidget(search: _searchContoller.text.trim());
} else {
return OtherRecipesWidget();
}
}
}
User_recipes.dart:
class UserRecipesWidget extends StatefulWidget {
UserRecipesWidget({super.key, required this.search});
String search;
#override
State<UserRecipesWidget> createState() => _UserRecipesWidgetState();
}
class _UserRecipesWidgetState extends State<UserRecipesWidget> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
var provider = context.watch<RecipeProvider>();
return FutureBuilder(
future: provider.getUserRecipesFuture,
builder: (BuildContext ctx, AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.connectionState == ConnectionState.done) {
if (asyncSnapshot.hasError) {
return const Center(child: Text('Could not retreive recipes!'));
}
return ListView.builder(
itemCount: provider.recipes.length,
itemBuilder: (BuildContext ctx, int index) {
return GestureDetector(
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => RecipePage(recipe: provider.recipes[index])
)
),
},
child: RecipeCard(recipe: provider.recipes[index]),
);
}
);
} else {
return Center(child: CircularProgressIndicator());
}
}
);
}
}
RecipeProvider:
class RecipeProvider extends ChangeNotifier {
late RecipeService _recipeService;
List<RecipeModel> recipes = [];
late Future getUserRecipesFuture;
final String uid;
RecipeProvider({ required this.uid }) {
_recipeService = RecipeService(uid: uid);
getUserRecipesFuture = _getUserRecipesFuture();
}
Future _getUserRecipesFuture() async {
recipes = await _recipeService.getUserRecipes();
addDummyRecipe();
}
addDummyRecipe() {
recipes.add(RecipeModel(uid: "test", userId: "test", recipeName: "Pork"));
recipes.add(RecipeModel(uid: "test1", userId: "test1", recipeName: "Pizza"));
recipes.add(RecipeModel(uid: "test2", userId: "test2", recipeName: "Burger"));
notifyListeners();
}
}
And the main one that gives the error, add_recipe.dart:
class AddRecipe extends StatefulWidget {
const AddRecipe({super.key});
#override
State<AddRecipe> createState() => _AddRecipeState();
}
class _AddRecipeState extends State<AddRecipe> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: 'Add recipe', backButton: true),
body: SafeArea(
child: Center(
child: Column(
children: [
Text('Add recipe'),
SizedBox(height: 50),
// Add recipe button
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: GestureDetector(
onTap: () async {
context.read<RecipeProvider>().addDummyRecipe();
},
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
'Save recipe',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
),
),
]
)
)
),
);
}
}
`
the exception happens because you provided the RecipeProvider only for the HomePage(). means when you push a new route, it doesn't have that the RecipeProvider instance.
if you can't provide the RecipeProvider Golobally because it needs the uid, then you must provide it each time you push a new route
change your RecipePage Navigator to this
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider.value(
value: context.read<RecipeProvider>(),
child: RecipePage(recipe: provider.recipes[index]),
),
),
),
and your AddRecipe
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider.value(
value: context.read<RecipeProvider>(),
child: AddRecipe(),
),
),
),

How to Pause and Resume the qr_code_scanner using the NavigationBar

I have a NavigationBar with 3 Destinations and I want to pause or resume the qr_code_scanner depending on what destination is focused, to do this i use a if to evaluate the index and pause or resume the scanner, but i dont now how pass the controller between classes.
My NavigationBar
bottomNavigationBar: NavigationBar(
elevation: 2,
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
selectedIndex: index,
onDestinationSelected: (index) {
setState(() => this.index = index);
if (index == 1) {
widget.controller!.resumeCamera();
} else {
widget.controller!.pauseCamera();
}
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.store_outlined),
selectedIcon: Icon(Icons.store),
label: 'Tienda',
),
NavigationDestination(
icon: Icon(Icons.qr_code_scanner_outlined),
selectedIcon: Icon(Icons.qr_code_scanner),
label: 'Escaner',
),
NavigationDestination(
icon: Icon(Icons.shopping_cart_outlined),
selectedIcon: Icon(Icons.shopping_cart),
label: 'Carrito',
),
],
),
The Home class
class Home extends StatefulWidget {
final QRViewController? controller;
const Home({Key? key, this.controller}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
My QrScan Class
class QRScanner extends StatefulWidget {
const QRScanner({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => QRScannerState();
}
class QRScannerState extends State<QRScanner> {
Barcode? result;
QRViewController? controller;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
#override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
controller!.pauseCamera();
}
controller!.resumeCamera();
}
#override
Widget build(BuildContext context) {
void home() {
Home(
controller: controller,
);
}
home();
return Scaffold(
body: Column(
children: <Widget>[
Expanded(child: buildQrView(context)),
Align(
alignment: Alignment.bottomCenter,
child: Container(
alignment: Alignment.center,
height: 100,
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.toggleFlash();
setState(() {});
},
child: FutureBuilder(
future: controller?.getFlashStatus(),
builder: (context, snapshot) {
if (snapshot.data == true) {
return const Icon(Icons.flash_on);
} else {
return const Icon(Icons.flash_off);
}
// Text('Flash: ${snapshot.data}');
},
)),
),
const Padding(padding: EdgeInsets.fromLTRB(37, 0, 0, 0)),
if (result != null)
Text('ID:${result!.code}')
else
const Text('Escanee el QR del producto'),
],
),
),
)
],
),
);
}
Widget buildQrView(BuildContext context) {
// For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
var scanArea = (MediaQuery.of(context).size.width < 400 ||
MediaQuery.of(context).size.height < 400)
? 150.0
: 300.0;
// To ensure the Scanner view is properly sizes after rotation
// we need to listen for Flutter SizeChanged notification and update controller
return QRView(
key: qrKey,
onQRViewCreated: onQRViewCreated,
overlay: QrScannerOverlayShape(
borderColor: Colors.white,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: scanArea),
onPermissionSet: (ctrl, p) => onPermissionSet(context, ctrl, p),
);
}
void onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
});
controller.scannedDataStream.listen((scanData) {
setState(() {
result = scanData;
});
controller.pauseCamera();
Stream<List<Products>> readProducts() => FirebaseFirestore.instance
.collection('products')
.where('id', isEqualTo: '${result!.code}')
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => Products.fromJson(doc.data()))
.toList());
Future<bool> onWillPop() async {
return false;
}
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) => WillPopScope(
onWillPop: () async => false,
child: WillPopScope(
onWillPop: onWillPop,
child: Dialog(
insetPadding: const EdgeInsets.all(8),
child: StreamBuilder<List<Products>>(
stream: readProducts(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Algo ha ocurrido! ${snapshot.error}');
} else if (snapshot.hasData) {
final products = snapshot.data!;
return Container(
margin: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: products
.map((p) => BuildProducts(
products: p, controller: controller))
.toList()),
);
} else {
return const Center(child: CircularProgressIndicator());
}
}),
),
),
),
);
});
}
void onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
if (!p) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('no Permission')),
);
}
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
}

Flutter State Management with Bloc/Cubit

for many of you this is an obvious / stupid question, but I've come to a point where I don't have a clue anymore. I have real difficulties understanding State Management with Bloc / Cubit.
Expectation: I have a page with a ListView (recipe_list) of all recipes and an 'add' button. Whenever I click on a ListItem or the 'add' button I go to the next page (recipe_detail). On this page I can create a new recipe (if clicked the 'add' button before), update or delete the existing recipe (if clicked on ListItem before). When I click the 'save' or 'delete' button the Navigator pops and I go back to the previous page (recipe_list). I used Cubit to manage the state of the recipe list. My expectation is that the ListView updates automatically after I clicked 'save' or 'delete'. But I have to refresh the App to display the changes.
main.dart
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Recipe Demo',
home: BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository())..getAllRecipes(),
child: const RecipeList(),
)
);
}
}
recipe_list.dart
class RecipeList extends StatefulWidget {
const RecipeList({Key? key}) : super(key: key);
#override
_RecipeListState createState() => _RecipeListState();
}
class _RecipeListState extends State<RecipeList> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 24.0
),
color: const Color(0xFFF6F6F6),
child: Stack(
children: [
Column(
children: [
Container(
margin: const EdgeInsets.only(
top: 32.0,
bottom: 32.0
),
child: const Center(
child: Text('Recipes'),
),
),
Expanded(
child: BlocBuilder<RecipeCubit, RecipeState>(
builder: (context, state) {
if (state is RecipeLoading) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (state is RecipeError) {
return const Center(
child: Icon(Icons.close),
);
} else if (state is RecipeLoaded) {
final recipes = state.recipes;
return ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository()),
child: RecipeDetail(recipe: recipes[index]),
);
}
));
},
child: RecipeCardWidget(
title: recipes[index].title,
description: recipes[index].description,
),
);
},
);
} else {
return const Text('Loading recipes error');
}
}
),
),
],
),
Positioned(
bottom: 24.0,
right: 0.0,
child: FloatingActionButton(
heroTag: 'addBtn',
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository()),
child: const RecipeDetail(recipe: null),
);
}
));
},
child: const Icon(Icons.add_rounded),
backgroundColor: Colors.teal,
),
)
],
),
),
),
);
}
}
recipe_detail.dart
class RecipeDetail extends StatefulWidget {
final Recipe? recipe;
const RecipeDetail({Key? key, required this.recipe}) : super(key: key);
#override
_RecipeDetailState createState() => _RecipeDetailState();
}
class _RecipeDetailState extends State<RecipeDetail> {
final RecipeRepository recipeRepository = RecipeRepository();
final int _recipeId = 0;
late String _recipeTitle = '';
late String _recipeDescription = '';
final recipeTitleController = TextEditingController();
final recipeDescriptionController = TextEditingController();
late FocusNode _titleFocus;
late FocusNode _descriptionFocus;
bool _buttonVisible = false;
#override
void initState() {
if (widget.recipe != null) {
_recipeTitle = widget.recipe!.title;
_recipeDescription = widget.recipe!.description;
_buttonVisible = true;
}
_titleFocus = FocusNode();
_descriptionFocus = FocusNode();
super.initState();
}
#override
void dispose() {
recipeTitleController.dispose();
recipeDescriptionController.dispose();
_titleFocus.dispose();
_descriptionFocus.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 24.0
),
color: const Color(0xFFF6F6F6),
child: Stack(
children: [
Column(
children: [
Align(
alignment: Alignment.topLeft,
child: InkWell(
child: IconButton(
highlightColor: Colors.transparent,
color: Colors.black54,
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(Icons.arrow_back_ios_new_rounded),
),
),
),
TextField(
focusNode: _titleFocus,
controller: recipeTitleController..text = _recipeTitle,
decoration: const InputDecoration(
hintText: 'Enter recipe title',
border: InputBorder.none
),
style: const TextStyle(
fontSize: 26.0,
fontWeight: FontWeight.bold
),
onSubmitted: (value) => _descriptionFocus.requestFocus(),
),
TextField(
focusNode: _descriptionFocus,
controller: recipeDescriptionController..text = _recipeDescription,
decoration: const InputDecoration(
hintText: 'Enter recipe description',
border: InputBorder.none
),
),
],
),
Positioned(
bottom: 24.0,
left: 0.0,
child: FloatingActionButton(
heroTag: 'saveBtn',
onPressed: () {
if (widget.recipe == null) {
Recipe _newRecipe = Recipe(
_recipeId,
recipeTitleController.text,
recipeDescriptionController.text
);
context.read<RecipeCubit>().createRecipe(_newRecipe);
//recipeRepository.createRecipe(_newRecipe);
Navigator.pop(context);
} else {
Recipe _newRecipe = Recipe(
widget.recipe!.id,
recipeTitleController.text,
recipeDescriptionController.text
);
context.read<RecipeCubit>().updateRecipe(_newRecipe);
//recipeRepository.updateRecipe(_newRecipe);
Navigator.pop(context);
}
},
child: const Icon(Icons.save_outlined),
backgroundColor: Colors.amberAccent,
),
),
Positioned(
bottom: 24.0,
right: 0.0,
child: Visibility(
visible: _buttonVisible,
child: FloatingActionButton(
heroTag: 'deleteBtn',
onPressed: () {
context.read<RecipeCubit>().deleteRecipe(widget.recipe!.id!);
//recipeRepository.deleteRecipe(widget.recipe!.id!);
Navigator.pop(context);
},
child: const Icon(Icons.delete_outline_rounded),
backgroundColor: Colors.redAccent,
),
),
),
],
),
),
),
);
}
}
recipe_state.dart
part of 'recipe_cubit.dart';
abstract class RecipeState extends Equatable {
const RecipeState();
}
class RecipeInitial extends RecipeState {
#override
List<Object> get props => [];
}
class RecipeLoading extends RecipeState {
#override
List<Object> get props => [];
}
class RecipeLoaded extends RecipeState {
final List<Recipe> recipes;
const RecipeLoaded(this.recipes);
#override
List<Object> get props => [recipes];
}
class RecipeError extends RecipeState {
final String message;
const RecipeError(this.message);
#override
List<Object> get props => [message];
}
recipe_cubit.dart
part 'recipe_state.dart';
class RecipeCubit extends Cubit<RecipeState> {
final RecipeRepository recipeRepository;
RecipeCubit(this.recipeRepository) : super(RecipeInitial()) {
getAllRecipes();
}
void getAllRecipes() async {
try {
emit(RecipeLoading());
final recipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(recipes));
} catch (e) {
emit(const RecipeError('Error'));
}
}
void createRecipe(Recipe recipe) async {
await recipeRepository.createRecipe(recipe);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
void updateRecipe(Recipe recipe) async {
await recipeRepository.updateRecipe(recipe);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
void deleteRecipe(int id) async {
await recipeRepository.deleteRecipe(id);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
}
It looks like you're creating another BlocProvider when you're navigating to RecipeDetail page. When you're pushing new MaterialPageRoute, this new page gets additionally wrapped in new RecipeCubit. Then, when you're calling context.read<RecipeCubit>(), you're referencing that provider (as this is closest BlocProvider in the widget tree). Your RecipeList can't react to those changes because it's BlocBuilder is looking for a BlocProvider declared above it in the widget tree (the one in MyApp).
Besides that, newly created provider gets removed from the widget tree anyway when you're closing RecipeDetail page as it is declared in the MaterialPageRoute which has just been pushed off the screen.
Try to remove the additional BlocProvider (the one in RecipeList, in OnTap function of RecipeCardWidget):
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>( // remove this BlocProvider
create: (context) => RecipeCubit(RecipeRepository()),
child: RecipeDetail(recipe: recipes[index]),
);
}
));
},

Flutter CupertinoPicker doesnt fit into screen

Im building my first flutter project and at the moment im facing a huge problem with CupertinoPicker. It appears to be fully functional, but i cannot embed it into Stepper correctly. I can only see one element at the time, not the whole selection wheel. Any ideas how to solve this ?
Almost entire code :
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'objects/Course.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int selectedCourse = 0;
List<Course> courses;
int _currentstep = 0;
double _yearFrom = 1, _yearTo = 5;
double _currentSliderValue = 1;
go(int step) {
setState(() => _currentstep += step);
print(selectedCourse);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Stepper(
currentStep: _currentstep,
onStepContinue: () {
if (_currentstep != 1 && selectedCourse >= 0) {
go(1);
} else {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text('Error !')));
}
},
onStepCancel: () {
if (_currentstep != 0) {
go(-1);
}
},
controlsBuilder: (BuildContext context,
{VoidCallback onStepContinue, VoidCallback onStepCancel}) {
return Center(
child: Row(
children: <Widget>[
RaisedButton(
padding: const EdgeInsets.all(10.0),
color: Colors.blue,
child: Text(
"Next",
style: TextStyle(color: Colors.white),
),
onPressed: onStepContinue,
),
FlatButton(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text("Back"),
),
onPressed: onStepCancel,
),
],
),
);
},
steps: [
Step(
title: Text("Pick your study program"),
isActive: _currentstep == 0,
content: Center(
child: FutureBuilder<List<Course>>(
future: downloadData(), // function where you call your api
builder: (BuildContext context,
AsyncSnapshot<List<Course>> snapshot) {
// AsyncSnapshot<Your object type>
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: Text('Please wait its loading...'));
} else {
if (snapshot.hasError)
return Center(child: Text('Error: ${snapshot.error}'));
else
return showCoursePicker();
// snapshot.data :- get your object which is pass from your downloadData() function
}
},
),
),
),
Step(
isActive: _currentstep == 1,
state: StepState.indexed,
title: const Text('Pick your study year'),
content: Column(
children: <Widget>[
Card(
child: Row(
children: [
Column(
children: [Text(_yearFrom.toInt().toString())],
),
Column(
children: [
Slider(
min: _yearFrom,
max: _yearTo == _yearFrom ? _yearTo + 1 : _yearTo,
divisions: _yearTo == _yearFrom
? _yearTo.toInt() - _yearFrom.toInt() + 1
: _yearTo.toInt() - _yearFrom.toInt(),
value: _currentSliderValue,
label: _currentSliderValue.round().toString(),
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
});
},
),
],
),
Column(
children: [
Text(_yearTo.toInt().toString()),
],
)
],
),
),
],
),
)
],
),
);
}
Future<List<Course>> downloadData() async {
var response = await http
.get('https://lekcijas.va.lv/lekcijas_android/getAllCourseData.php');
var data = json.decode(response.body)["result"];
courses = List<Course>.from(data.map((x) => Course.fromJson(x)));
return Future.value(courses);
}
Widget showCoursePicker() {
return CupertinoPicker.builder(
childCount: courses.length,
itemExtent: 40,
useMagnifier: true,
magnification: 1.3,
onSelectedItemChanged: (value) {
selectedCourse = value;
print(selectedCourse);
setState(() {
_yearFrom = double.parse(courses[selectedCourse].course_from);
print(_yearFrom);
_yearTo = double.parse(courses[selectedCourse].course_to);
print(_yearTo);
});
},
itemBuilder: (ctx, index) {
return Center(
child: Text(
courses.isEmpty ? '' : courses[index].abbreviation,
),
);
});
}
}
Course object code :
class Course {
final String id;
final String abbreviation;
final String course_from;
final String course_to;
Course({this.id, this.abbreviation, this.course_from, this.course_to});
factory Course.fromJson(Map<String, dynamic> json) {
return Course(
id: json['id'] as String,
abbreviation: json['abbreviation'] as String,
course_from: json['course_from'] as String,
course_to: json['course_to'] as String,
);
}
}
Pic :
Layout with CupertinoPicker (only magnifier is visible)
Note : You need to add dependencies in pubspec.yml :
dependencies:
http: ^0.12.2
You can copy paste run full code below
Step 1: Set Future like this to avoid rebuild cause reload data
Future<List<Course>> _future;
...
#override
void initState() {
_future = downloadData();
...
child: FutureBuilder<List<Course>>(
future: _future,
Step 2: Use Container to set height
Widget showCoursePicker() {
return Container(
height: 250,
child: CupertinoPicker.builder(
working demo
full code
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class Course {
final String id;
final String abbreviation;
final String course_from;
final String course_to;
Course({this.id, this.abbreviation, this.course_from, this.course_to});
factory Course.fromJson(Map<String, dynamic> json) {
return Course(
id: json['id'] as String,
abbreviation: json['abbreviation'] as String,
course_from: json['course_from'] as String,
course_to: json['course_to'] as String,
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int selectedCourse = 0;
List<Course> courses;
int _currentstep = 0;
double _yearFrom = 1, _yearTo = 5;
double _currentSliderValue = 1;
Future<List<Course>> _future;
go(int step) {
setState(() => _currentstep += step);
print(selectedCourse);
}
#override
void initState() {
_future = downloadData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Stepper(
currentStep: _currentstep,
onStepContinue: () {
if (_currentstep != 1 && selectedCourse >= 0) {
go(1);
} else {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text('Error !')));
}
},
onStepCancel: () {
if (_currentstep != 0) {
go(-1);
}
},
controlsBuilder: (BuildContext context,
{VoidCallback onStepContinue, VoidCallback onStepCancel}) {
return Center(
child: Row(
children: <Widget>[
RaisedButton(
padding: const EdgeInsets.all(10.0),
color: Colors.blue,
child: Text(
"Next",
style: TextStyle(color: Colors.white),
),
onPressed: onStepContinue,
),
FlatButton(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text("Back"),
),
onPressed: onStepCancel,
),
],
),
);
},
steps: [
Step(
title: Text("Pick your study program"),
isActive: _currentstep == 0,
content: Center(
child: FutureBuilder<List<Course>>(
future: _future, // function where you call your api
builder: (BuildContext context,
AsyncSnapshot<List<Course>> snapshot) {
// AsyncSnapshot<Your object type>
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: Text('Please wait its loading...'));
} else {
if (snapshot.hasError)
return Center(child: Text('Error: ${snapshot.error}'));
else
return showCoursePicker();
// snapshot.data :- get your object which is pass from your downloadData() function
}
},
),
),
),
Step(
isActive: _currentstep == 1,
state: StepState.indexed,
title: const Text('Pick your study year'),
content: Column(
children: <Widget>[
Card(
child: Row(
children: [
Column(
children: [Text(_yearFrom.toInt().toString())],
),
Column(
children: [
Slider(
min: _yearFrom,
max: _yearTo == _yearFrom ? _yearTo + 1 : _yearTo,
divisions: _yearTo == _yearFrom
? _yearTo.toInt() - _yearFrom.toInt() + 1
: _yearTo.toInt() - _yearFrom.toInt(),
value: _currentSliderValue,
label: _currentSliderValue.round().toString(),
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
});
},
),
],
),
Column(
children: [
Text(_yearTo.toInt().toString()),
],
)
],
),
),
],
),
)
],
),
);
}
Future<List<Course>> downloadData() async {
var response = await http
.get('https://lekcijas.va.lv/lekcijas_android/getAllCourseData.php');
var data = json.decode(response.body)["result"];
courses = List<Course>.from(data.map((x) => Course.fromJson(x)));
return Future.value(courses);
}
Widget showCoursePicker() {
return Container(
height: 250,
child: CupertinoPicker.builder(
childCount: courses.length,
itemExtent: 40,
useMagnifier: true,
magnification: 1.3,
onSelectedItemChanged: (value) {
selectedCourse = value;
print(selectedCourse);
setState(() {
_yearFrom = double.parse(courses[selectedCourse].course_from);
print(_yearFrom);
_yearTo = double.parse(courses[selectedCourse].course_to);
print(_yearTo);
});
},
itemBuilder: (ctx, index) {
return Center(
child: Text(
courses.isEmpty ? '' : courses[index].abbreviation,
),
);
}),
);
}
}

PushNamed issue: Type 'FillData' (a Statefulwidget) is not a subtype of type 'List<Object>'

I'm new in Flutter. I'm trying to push a List from NewData to FillData screen with pushNamed. But it said:
The following _TypeError was thrown while handling a gesture:
type 'FillData' is not a subtype of type 'List'
If i remove the comment in '/FillData', i receive null data instead. What should i do?
This is my code:
SettingNavigator
class SettingNavigator extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => Home(),
'/NewData': (context) => NewData(),
// '/FillData': (context) => FillData(), (in comment)
}
onGenerateRoute: (setting) {
if (setting.name == '/FillData') {
final ChartGroupData chartName = setting.arguments;
final List<ChartGroupData> groupNames = setting.arguments;
return MaterialPageRoute(builder: (context) {
return FillData(
chartName: chartName,
gName: groupNames,
);
});
}
return null;
},
);
}
}
NewData
import 'package:flutter/material.dart';
class NewData extends StatefulWidget {
List<ChartGroupData> groupNames;
NewData({Key key, #required this.groupNames}) : super(key: key);
#override
NewDataStage createState() => NewDataStage();
}
class NewDataStage extends State<NewData> {
TextEditingController _nameCtrl = new TextEditingController();
var textFields = <Widget>[];
var groupTECs = <TextEditingController>[];
#override
void initState() {
super.initState();
textFields.add(createCustomTextField());
}
Widget createCustomTextField() {
var groupCtrl = TextEditingController();
groupTECs.add(groupCtrl);
return Container(
padding: EdgeInsets.fromLTRB(0, 5, 0, 0),
child: Row(
children: <Widget>[
Expanded(flex: 3, child: Text("Group ${textFields.length}")),
Container(
constraints: BoxConstraints.tightFor(width: 120, height: 60),
child: TextField(
controller: groupCtrl,
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(child: Text("New Chart")),
),
body: Container(
alignment: AlignmentDirectional.center,
constraints: BoxConstraints.expand(),
child: Column(
children: <Widget>[
Text(
"Your chart name",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
TextField(
style: TextStyle(fontSize: 20),
controller: _nameCtrl,
),
Expanded(
flex: 3,
child: Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: textFields.length,
itemBuilder: (BuildContext context, int index) {
return textFields[index];
},
),
),
),
SizedBox(
height: 60,
width: 120,
child: RaisedButton(
onPressed: _onTapNext,
child: Text("NEXT"),
color: Colors.green,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _onTapCreate,
child: Icon(Icons.add, color: Colors.white),
shape: CircleBorder(),
),
),
);
}
void _onTapNext() {
/// Push Groups name to FillData
widget.groupNames = List<ChartGroupData>();
for (int i = 0; i < textFields.length; i++) {
var name = groupTECs[i].text;
widget.groupNames.add(ChartGroupData(name));
}
print(widget.groupNames.toString());
Navigator.pushNamed(context, '/FillData',
arguments: FillData(
gName: widget.groupNames,
chartName: ChartGroupData(_nameCtrl.text),
));
}
void _onTapCreate() {
setState(() {
textFields.add(createCustomTextField());
});
}
}
FillData
class FillData extends StatefulWidget {
final ChartGroupData chartName;
final List<ChartGroupData> gName;
FillData({Key key, #required this.chartName, #required this.gName})
: super(key: key);
#override
FillDataStage createState() => FillDataStage();
}
class FillDataStage extends State<FillData> {
void _showDialog() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Received Data"),
content: Text(widget.chartName.toString()),
);
},
);
}
void _onTapPrintReceivedData() {
print(widget.gName);
print(widget.chartName);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(
child: Text("Fill your Data"),
),
),
body: Center(
child: RaisedButton(
onPressed: () {
_onTapPrintReceivedData();
_showDialog();
},
child: Text("Print Data"),
),
),
));
}
}
Class ChartGroupData
lass ChartGroupData {
final String groupNames;
ChartGroupData(this.groupNames);
#override
String toString() {
return 'Group: $groupNames';
}
}
You have 2 problems with your code:
1- you cant user routes with onGenerateRoute, because now the app doesn't know where to go, to the widget that you didn't pass anything to (inside routes) or to the widget inside the onGenerateRoute.
2- arguments is a general object that you can put whatever you want inside of it, and doing this:
final ChartGroupData chartName = setting.arguments; final
List groupNames = setting.arguments;
passes the same value to two different objects, I solved this by doing the following (it's not the best but will give you a rough idea of what you should do)
created a new object that contains the data to be passed:
class ObjectToPass {
final ChartGroupData chartName;
final List<ChartGroupData> groupNames;
ObjectToPass({this.chartName, this.groupNames});
}
changed FillData implementation:
class FillData extends StatefulWidget {
final ObjectToPass objectToPass;
FillData({Key key, #required this.objectToPass}) : super(key: key);
#override
FillDataStage createState() => FillDataStage();
}
...
void _showDialog() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Received Data"),
content: Text(widget.objectToPass.chartName.toString()),
);
},
);
}
void _onTapPrintReceivedData() {
print(widget.objectToPass.groupNames);
print(widget.objectToPass.chartName);
}
to navigate to FillData you would:
Navigator.pushNamed(
context,
'/FillData',
arguments: ObjectToPass(
chartName: ChartGroupData(_nameCtrl.text),
groupNames: groupNames,
),
);
finally this is how your MaterialApp should look like:
return MaterialApp(
initialRoute: '/NewData',
onGenerateRoute: (setting) {
if (setting.name == '/FillData') {
return MaterialPageRoute(builder: (context) {
return FillData(
objectToPass: setting.arguments,
);
});
} else if (setting.name == '/NewData') {
return MaterialPageRoute(builder: (_) => NewData());
}
return null;
},
);
you can pass a list instead of the object I created and get your objects from it by it's index.