Flutter Bloc State Success State Not woking - flutter

I want Popup dialog or Card when state.success is NotEmpty
When success state is empty it return Text as i set in code
I don't know, Widget error or Other
I need help
import 'package:bloc_salesearch/src/screen/page_settings.dart';
import 'package:bloc_salesearch/src/search_bloc/bloc.dart';
import 'package:bloc_salesearch/src/util/utils.dart';
import 'package:bloc_salesearch/src/widget/utils_widget.dart';
import 'package:flutter/material.dart';
import 'package:bloc_salesearch/src/widget/widgets.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../src.dart';
class SearchPage extends StatefulWidget {
#override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
Screen size;
#override
void dispose() {
// TODO: implement dispose
super.dispose();
}
#override
Widget build(BuildContext context) {
size = Screen(MediaQuery.of(context).size);
return Scaffold(
backgroundColor: backgroundColor,
body: AnnotatedRegion(
value: SystemUiOverlayStyle(
statusBarColor: backgroundColor,
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: backgroundColor,
systemNavigationBarIconBrightness: Brightness.dark,
),
child: Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
upperPart(),
],
),
),
),
),
);
}
Widget upperPart() {
return Stack(
children: <Widget>[
ClipPath(
clipper: UpperClipper(),
child: Container(
height: size.getWidthPx(240),
decoration: BoxDecoration(
gradient:
LinearGradient(colors: [colorCurve, colorCurveSecondary]),
),
),
),
Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: size.getWidthPx(36)),
child: Column(
children: <Widget>[
titleWidget(),
SizedBox(
height: size.getWidthPx(10),
),
upperBoxCard(),
SizedBox(
height: size.getWidthPx(10),
),
SearchResult(),
],
),
),
],
),
],
);
}
Text titleWidget() {
return Text("Search Lottery",
style: TextStyle(
// fontFamily: 'Exo2',
fontSize: 24.0,
fontWeight: FontWeight.w900,
color: Colors.white));
}
Card upperBoxCard() {
return Card(
elevation: 4.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
margin: EdgeInsets.symmetric(
horizontal: size.getWidthPx(20), vertical: size.getWidthPx(16)),
borderOnForeground: true,
child: Container(
height: size.getWidthPx(150),
child: Column(
children: <Widget>[
SearchWidget(),
leftAlignText(
text: "Your Number foo win :",
leftPadding: size.getWidthPx(16),
textColor: textPrimaryColor,
fontSize: 16.0),
rightAlignText(
text: '150000',
rightPadding: size.getWidthPx(16),
textColor: textPrimaryColor,
fontSize: 16.0)
],
),
));
}
//
}
class SearchResult extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<SearchBloc, SearchState>(
builder: (context, state) {
if (state is SearchStateLoading) {
return Container(
height: 40,
child: Center(
child: CircularProgressIndicator(),
),
);
}
if (state is SearchStateError) {
return Text(state.error);
}
if (state is SearchStateSuccess) {
print(state);
return state.items.isEmpty
? Text('No Found!Good Luck Next Month!')
: Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=> SettingPage()));
}
return Text('Please Type your Lottery Number To begin');
},
);
}
}
class _SearchResult extends StatelessWidget {
final List<Sale> items;
const _SearchResult({Key key, this.items}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return _SearchView(item: items[index]);
},
);
}
}
class _SearchView extends StatelessWidget {
final Sale item;
const _SearchView({Key key, this.item}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(child: Text(''
'You Win'),);
}
}
class SearchWidget extends StatefulWidget {
#override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final texEdc = TextEditingController();
SearchBloc _searchBloc;
#override
void initState() {
// TODO: implement initState
super.initState();
_searchBloc = BlocProvider.of<SearchBloc>(context);
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
texEdc.dispose();
}
#override
Widget build(BuildContext context) {
return TextField(
controller: texEdc,
autocorrect: false,
onChanged: (text) {
_searchBloc.dispatch(
TextChanged(text: text),
);
},
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
suffixIcon: GestureDetector(
child: Icon(Icons.clear),
onTap: _onClearTapped,
),
border: InputBorder.none,
hintText: 'Enter your Lottery number',
),
);
}
void _onClearTapped() {
texEdc.text = '';
_searchBloc.dispatch(TextChanged(text: ''));
}
}

You can listen to Bloc state with the BlocListener and execute actions depending on different states.
You should wrap your widget or bloc builder into a BlocListener as this:
BlocListener(
bloc: _searchBloc,
listener: (BuildContext context, SearchState state) {
if(state is SearchStateSuccess){
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=> SettingPage()));
}
},
child: your_blocBuilder
)

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 refresh data after returning to a screen in flutter

I have the following cart screen which contains the address fields. When a user wants to update their address, they click on Update button, which takes them to the address screen where they can update the address. Although when the back button is clicked to return back to the cart screen,the print function shows the updated value but the UI still shows the old value.
class CartScreen extends StatefulWidget {
static const routeName = '/cart';
#override
_CartScreenState createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
Future _addressFuture;
String value = 'deliver';
var address;
void initState() {
_addressFuture = _obtainAddressFuture();
super.initState();
}
Future _obtainAddressFuture() async {
address = await Provider.of<Addresses>(context, listen: false).fetchMyAddress();
}
void didChangeDependencies() async {
print('inside didchange');
super.didChangeDependencies();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
print('Rebuild cart');
return GestureDetector(
child: Scaffold(
appBar: AppBar(
title: Text('Your Cart'),
),
body: FutureBuilder(
future: _addressFuture,
builder: (ctx, dataSnapshot) {
if (dataSnapshot.error != null) {
print('Data snapshot error is ' +
dataSnapshot.error.toString());
return Center(
child: Text('Something went wrong!'),
);
} else if (dataSnapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: Loading(),
);
} else {
return Container(
child: SingleChildScrollView(
child: Column(
children: [
Card(
margin: EdgeInsets.symmetric(
horizontal: 15,
vertical: 4,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
new GestureDetector(
onTap: () async{
_awaitReturnValueFromSecondScreen(context);
},
child: Container(
padding: EdgeInsets.all(10),
child: const Text("Update",
style: TextStyle(
color: Colors.blue))),
)
],
),
TextFormField(
initialValue: address.name,
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(Icons.person,
color: Colors.orangeAccent),
labelText: 'Contact name'),
),
],
)),
],
)));
}
})));
}
void _awaitReturnValueFromSecondScreen(BuildContext context) async {
// start the SecondScreen and wait for it to finish with a result
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddressScreen('cartScreen'),
));
setState(() {
address = result;
});
print(address.name);
}
}
please check the below example as it requires async and await to get the data from 2nd screen
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(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(
title: 'Flutter Location',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: Provider(
create: (context) => Addresses(),
child: CartScreen(),
),
);
}
}
class CartScreen extends StatefulWidget {
static const routeName = '/cart';
#override
_CartScreenState createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
Future? _addressFuture;
String value = 'deliver';
var address;
void initState() {
_addressFuture = _obtainAddressFuture();
super.initState();
}
Future _obtainAddressFuture() async {
address =
await Provider.of<Addresses>(context, listen: false).fetchMyAddress();
}
void didChangeDependencies() async {
super.didChangeDependencies();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Scaffold(
appBar: AppBar(
title: Text('Your Cart'),
),
body: FutureBuilder(
future: _addressFuture,
builder: (ctx, dataSnapshot) {
if (dataSnapshot.error != null) {
print('Data snapshot error is ' + dataSnapshot.error.toString());
return const Center(
child: Text('Something went wrong!'),
);
} else if (dataSnapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
return SingleChildScrollView(
child: Column(
children: [
Card(
margin: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 4,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () async {
// Here you have to wait for the screen to retrun data so we use asyn await
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
AddressScreen(title: 'cartScreen'),
),
);
print(result);
},
child: Container(
padding: EdgeInsets.all(10),
child: const Text(
"Update",
style: TextStyle(
color: Colors.blue,
),
),
),
)
],
),
TextFormField(
// initialValue: address.name,
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(Icons.person,
color: Colors.orangeAccent),
labelText: 'Contact name',
),
),
],
),
),
],
),
);
}
},
),
),
);
}
}
class AddressScreen extends StatelessWidget {
const AddressScreen({
Key? key,
required this.title,
}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: TextButton(
onPressed: () {
// Here you have pass the data that you want
Navigator.pop(context, 'Data pass here ');
},
child: Text('Pass data back'),
),
),
);
}
}
class Addresses {
Future fetchMyAddress() async {}
}

How to add CircularProgressIndicator at the end of listview while waiting for request

I would like to have CircularProgressIndicator at the end of list if request for another portion of advertisements is being loaded. I guess it needs to be done under onNotification method, because there I make the request and maybe disable it when this method is done?
The code is similar to https://codinginfinite.com/flutter-future-builder-pagination/
Could you tell me how can I do it?
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:..../ui/pages/home/page/AdvertisementCard.dart';
import 'package:.../ui/pages/home/page/model/AdvertisementList.dart';
import '../../SizedBox.dart';
import 'AdvertisementProdRepository.dart';
import 'BottomAppBar.dart';
import 'FAB.dart';
import 'model/AdvertisementList.dart';
class HomePage extends StatefulWidget {
final String jwt;
const HomePage(this.jwt);
#override
_HomePage createState() => _HomePage();
factory HomePage.fromBase64(String jwt) => HomePage(jwt);
}
class _HomePage extends State<HomePage> {
late final String jwt;
late Future<AdvertisementList> _listOfItems;
final searchTextController = TextEditingController();
#override
void initState() {
super.initState();
jwt = widget.jwt;
_listOfItems = AdvertisementProdRepository.fetchAdvertisements(1);
}
#override
Widget build(BuildContext context) => Scaffold(
body: Scaffold(
backgroundColor: const Color(0xFEF9F9FC),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: buildFAB(),
bottomNavigationBar: BuildBottomAppBar(),
body: Container(
padding: EdgeInsets.only(left: 25.0, right: 25, top: 25),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: [
TextFormField(
controller: searchTextController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Szukaj',
fillColor: Color(0xffeeeeee),
filled: true),
),
buildSizedBox(20.0),
Padding(
padding: const EdgeInsets.only(left: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Najnowsze ogłoszenia',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
],
),
),
buildSizedBox(10.0),
FutureBuilder<AdvertisementList>(
future: _listOfItems,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return Expanded(
child:
AdvertisementTile(advertisements: snapshot.data!),
);
}
},
),
],
),
),
),
);
}
class AdvertisementTile extends StatefulWidget {
final AdvertisementList advertisements;
AdvertisementTile({Key? key, required this.advertisements}) : super(key: key);
#override
State<StatefulWidget> createState() => AdvertisementTileState();
}
class AdvertisementTileState extends State<AdvertisementTile> {
AdvertisementLoadMoreStatus loadMoreStatus =
AdvertisementLoadMoreStatus.STABLE;
final ScrollController scrollController = new ScrollController();
late List<Advertisement> advertisements;
late int currentPageNumber;
bool _loading = false;
#override
void initState() {
advertisements = widget.advertisements.items;
currentPageNumber = widget.advertisements.pageNumber;
super.initState();
}
#override
void dispose() {
scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: onNotification,
child: Padding(
padding: const EdgeInsets.only(bottom: 28.0),
child: new ListView.separated(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
controller: scrollController,
itemCount: advertisements.length,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (_, index) {
return AdvertisementCard(data: advertisements[index]);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 10,
);
},
),
),
);
}
bool onNotification(ScrollNotification notification) {
if (notification is ScrollUpdateNotification) {
if (scrollController.position.maxScrollExtent > scrollController.offset &&
scrollController.position.maxScrollExtent - scrollController.offset <=
50) {
if (loadMoreStatus == AdvertisementLoadMoreStatus.STABLE) {
loadMoreStatus = AdvertisementLoadMoreStatus.LOADING;
AdvertisementProdRepository.fetchAdvertisements(currentPageNumber + 1)
.then((advertisementObject) {
currentPageNumber = advertisementObject.pageNumber;
loadMoreStatus = AdvertisementLoadMoreStatus.STABLE;
setState(() => advertisements.addAll(advertisementObject.items));
});
}
}
}
return true;
}
}
enum AdvertisementLoadMoreStatus { LOADING, STABLE }
You can take a variable and set it as false.
loadingNewData = false;
You can then wrap your listview with a column. After listview you can add the conditional code.
Column(children: [
Listview(),
if (loadingNewData) CircularProgressIndicator()
])
Now whenever you reach the end of listview, you can set the loadingNewData as true and after the data is loaded you can set it back to false.

Bottom TextField with SnackBar

I want to Create an TextField at the bottom of the page like message app.
There is also a IconButton, which adds the entered text into ListView if TextField is not empty. If it is empty then it will show error in SnackBar.
The Problem is the SnackBar stacks on top of TextField. But I want it to be either top or bottom of TextField.
import 'package:flutter/material.dart';
Future<void> main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final _textList = <String>[];
TextEditingController _textController;
bool _addText(context, String text) {
print(text);
if (text?.isNotEmpty == true) {
setState(() {
_textList.add(text);
});
return true;
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text("Invalid Text Entered"),
behavior: SnackBarBehavior.fixed,
),
);
return false;
}
}
#override
void initState() {
_textController = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Demo"),
),
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: ListView.separated(
itemCount: _textList.length,
separatorBuilder: (_, __) => Divider(height: 1.0),
itemBuilder: (context, index) => ListTile(
title: Text("${_textList[index]}"),
),
),
),
_buildBottom(),
],
),
),
);
}
Widget _buildBottom() {
return Material(
elevation: 5.0,
color: Colors.blue[100],
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _textController,
decoration: InputDecoration(
hintText: "Enter Text",
contentPadding: EdgeInsets.symmetric(horizontal: 10.0),
border: InputBorder.none,
),
),
),
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.add),
onPressed: () {
final success = _addText(context, _textController.text);
if (success) _textController.clear();
},
),
),
],
),
);
}
}
This is my code in DartPad
try this,
import 'package:flutter/material.dart';
Future<void> main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final _textList = <String>[];
TextEditingController _textController;
bool isVisible = false;
bool _addText(context, String text) {
print(text);
if (text?.isNotEmpty == true) {
setState(() {
_textList.add(text);
});
return true;
} else {
Scaffold.of(context).showSnackBar(
new SnackBar(
content: Text("Invalid Text Entered"),
behavior: SnackBarBehavior.fixed,
duration: Duration(seconds: 3),
onVisible: (() {
setState(() {
isVisible = true;
});
Future.delayed(Duration(seconds: 3)).then((_) => setState(() {
isVisible = false;
}));
}),
),
);
return false;
}
}
#override
void initState() {
_textController = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Demo"),
),
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: ListView.separated(
itemCount: _textList.length,
separatorBuilder: (_, __) => Divider(height: 1.0),
itemBuilder: (context, index) => ListTile(
title: Text("${_textList[index]}"),
),
),
),
AnimatedContainer(
margin: EdgeInsets.only(bottom: isVisible ? 50 : 0),
child: _buildBottom(),
duration: Duration(milliseconds: 100),
),
],
),
),
);
}
Widget _buildBottom() {
return Material(
elevation: 5.0,
color: Colors.blue[100],
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _textController,
decoration: InputDecoration(
hintText: "Enter Text",
contentPadding: EdgeInsets.symmetric(horizontal: 10.0),
border: InputBorder.none,
),
),
),
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.add),
onPressed: () {
final success = _addText(context, _textController.text);
if (success) _textController.clear();
},
),
),
],
),
);
}
}
Perhaps using Flushbar might help with your problem. There are many properties that you can change, such as flushbarPosition.
It might not solve your problem exactly how you would expect it to but it can make the Flushbar appear from the top instead of the bottom and that's one way around your problem
Flushbar: https://pub.dev/packages/flushbar
I think the best thing you can do here is to change the behaviour to floating
SnackBar(
behavior: SnackBarBehavior.floating,
...

Why couldn't my onPressed callback call my Bloc using Bloc?

I'm using the Bloc Library by felangel, specifically Flutter_bloc, but using BlocProvider I'm unable to pass the bloc instance to my onpressed method for when a button is pressed
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'bloc/bloc.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bloc Demo',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double temp;
String _formText = "";
final _formKey = GlobalKey<FormState>(debugLabel: "City name should be here");
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello, title here"),
),
body: BlocProvider<WeatherBloc>(
builder: (BuildContext context) => WeatherBloc(),
child: BlocBuilder<WeatherBloc, WeatherState>(
builder: (BuildContext context, WeatherState state) {
if (state is InitialWeatherState) {
return buildInitialWeather();
}
else if (state is WeatherStateLoading) {
return buildLoading();
} else if (state is WeatherStateLoaded) {
return buildColumn(state.weather);
} else throw Exception("Something went wrong here");
}
),
),
);
}
Widget buildInitialWeather() {
return Center(
child: Column(children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0),
child: Form(
key: _formKey,
child: TextFormField(
decoration: InputDecoration(hintText: "Enter your city"),
onSaved: (value) {
setState(() {
_formText = value;
});
},
)),
),
new RaisedButton(
onPressed: _onPressed,
child: Text("Click Me!"),
splashColor: Colors.pink,
)
],),
);
}
Widget buildLoading() {
return Center(
child: CircularProgressIndicator(),
);
}
Column buildColumn(Weather weather) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Geo Locator for:'+weather.cityName,
style: TextStyle(fontSize: 20.0),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
weather.temp.toString(),
style: TextStyle(fontSize: 20.0),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32.0),
child: Form(
key: _formKey,
child: TextFormField(
decoration: InputDecoration(hintText: "Enter your city"),
onSaved: (value) {
setState(() {
_formText = value;
});
},
)),
),
new RaisedButton(
onPressed: _onPressed,
child: Text("Click Me!"),
splashColor: Colors.pink,
)
],
);
}
void _onPressed() {
_formKey.currentState.save();
print(_formText);
// This line fails. I don't know why.
BlocProvider.of<WeatherBloc>(context).dispatch(GetWeather(_formText));
}
#override
void dispose() {
super.dispose();
}
}
Basically whenever I run the code it throws the following exception: BlocProvider.of() called with a context that does not contain a Bloc of type Bloc.
Try to put BlocProvider in top of your project (Parent of MyHomePage or MaterialApp).