I tried to read data using shared preferences and return my widget using FutureBuilder, but the problem is... when I click another tab screen and then back again to my home tab screen, the data inside widget always rerender.. is there a way so that my data always stick in my widget whenever I click another tab and back again to my home tab?
here is the code
class _SecondPageState extends State<SecondPage>
with AutomaticKeepAliveClientMixin<SecondPage> {
Future<int> _getUsername;
#override
bool get wantKeepAlive => true;
#override
void initState() {
_getUsername = SharedPreferences.getInstance().then((prefs) {
return prefs.getInt("username");
});
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getUsername,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
print("loading");
return Text("Loading username...");
// how to prevent this condition so that my data always stick in my widget without rerender
}
final username = snapshot.data.toString();
return Container(
child: Text("Hello $username"),
);
});
}
}
You can copy paste run full code below
You can use AutomaticKeepAliveClientMixin
code snippet
class _Page3State extends State<Page3>
with AutomaticKeepAliveClientMixin<Page3> {
...
#override
bool get wantKeepAlive => true;
FutureBuilder(
future: _getUsername,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
print("loading");
return Text("Loading username...");
Working demo, you can see console output only print one loading
Output
I/flutter ( 8528): loading
full code
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MainPersistentTabBar(),
);
}
}
class MainPersistentTabBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
isScrollable: true,
tabs: [
Tab(
icon: Icon(Icons.directions_car),
text: "Non persistent",
),
Tab(icon: Icon(Icons.directions_transit), text: "Persistent"),
Tab(icon: Icon(Icons.directions_railway), text: "Test 1"),
Tab(icon: Icon(Icons.directions_subway), text: "Test 2"),
],
),
title: Text('Persistent Tab Demo'),
),
body: TabBarView(
children: [
Page1(),
Page2(),
Page3(),
Text('Page 4'),
],
),
),
);
}
}
class Page3 extends StatefulWidget {
#override
_Page3State createState() => _Page3State();
}
class _Page3State extends State<Page3>
with AutomaticKeepAliveClientMixin<Page3> {
Future<int> _getUsername;
#override
bool get wantKeepAlive => true;
#override
void initState() {
_getUsername = SharedPreferences.getInstance().then((prefs) {
return prefs.getInt("username");
});
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getUsername,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
print("loading");
return Text("Loading username...");
// how to prevent this condition so that my data always stick in my widget without rerender
}
final username = snapshot.data.toString();
return Container(
child: Text("Hello $username"),
);
});
}
}
class Page1 extends StatefulWidget {
#override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
var list = List();
_loadList() async {
final response =
await http.get("https://jsonplaceholder.typicode.com/posts/");
if (response.statusCode == 200) {
await new Future.delayed(const Duration(seconds: 1));
if (mounted) {
setState(() {
list = json.decode(response.body) as List;
});
}
} else {
throw Exception('Failed to load posts');
}
}
#override
void initState() {
_loadList();
super.initState();
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
final data = list[index];
return ListTile(
contentPadding: EdgeInsets.all(10.0),
title: Text(data['title']),
subtitle: Text(
data['body'],
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
);
},
);
}
}
class Page2 extends StatefulWidget {
#override
_Page2State createState() => _Page2State();
}
class _Page2State extends State<Page2>
with AutomaticKeepAliveClientMixin<Page2> {
var list = List();
_loadList() async {
final response =
await http.get("https://jsonplaceholder.typicode.com/photos/");
if (response.statusCode == 200) {
await new Future.delayed(const Duration(seconds: 1));
if (mounted) {
setState(() {
list = json.decode(response.body) as List;
});
}
} else {
throw Exception('Failed to load posts');
}
}
#override
void initState() {
_loadList();
super.initState();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Container(
child: ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
final data = list[index];
return ListTile(
contentPadding: EdgeInsets.all(10.0),
title: Text(data['title']),
trailing: Image.network(
data['thumbnailUrl'],
height: 20.0,
width: 20.0,
),
);
},
),
);
}
// TODO: implement wantKeepAlive
#override
bool get wantKeepAlive => true;
}
Related
I am trying to setup some check before the user can get access to his data.
I would like first to check if there is an internet connection. If so, then and only then I want to await Firebase.initializeApp();
But right now, I am not able to setup all this properly. Here is the code. If you could comment to help me it would be great. Thank you.
bool hasInternet = false;
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (context)=> EventProvider(),
child:LayoutBuilder(
builder: (context, constraints){
return OrientationBuilder(
builder: (context, orientation) {
SizeConfig().init(constraints, orientation);
return MaterialApp(
title: "",
home: const MainPage(),
theme: ThemeData(primarySwatch: Colors.blue),
routes: {
"/today": (context) => const Today_Home(),
"/agenda": (context) => const AgendaOrganize(),
}
);
},
);
},
));
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
late StreamSubscription subscription;
late StreamSubscription internetSubscription;
bool hasInternet = false;
ConnectivityResult result = ConnectivityResult.none;
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
#override
void initState() {
super.initState();
subscription = Connectivity().onConnectivityChanged.listen((result) {
setState(() => this.result = result);
});
internetSubscription =
InternetConnectionChecker().onStatusChange.listen((status) {
final hasInternet = status == InternetConnectionStatus.connected;
setState(() => this.hasInternet = hasInternet);
if (hasInternet != true) {
showSnackBarMsg(context, noInternet,10);
} else {return;}
});
}
#override
void dispose() {
subscription.cancel();
internetSubscription.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _initialization,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Scaffold(
body: Center(
child: Text("Error: ${snapshot.error}"),
),
);
}
if (snapshot.connectionState == ConnectionState.done) {
return StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
Object? user = snapshot.data;
if (user == null) {
return const LoginSignupScreen(); ///If user not connected => go to login screen
}
else {
return const Today_Home(); ///Else, go to Today_Home Page
}
}
return const Scaffold(
body: Center(
child: Text("Checking Login..."),
),
);
},
);
}
return const Scaffold(
body: Center(
child: Text("Connecting to the app..."),
),
);
},
);
}
}
I'm trying to pass the same pad that brings me a firebaseuser instance to two different screens using flutter_bloc, the home screen shows it to me successfully the problem this when I switch to the other screen this stays in waiting and does not go from there.
UserBloc Class
class UserBloc extends Bloc {
final _auth_repository = FirebaseAuthApiRespository();
Stream<FirebaseUser> streamFirebase = FirebaseAuth.instance.onAuthStateChanged;
Stream<FirebaseUser> get auhtStatus => streamFirebase;
Future<FirebaseUser> signIn() => _auth_repository.signInFirebase();
signOut() {
_auth_repository.signOut();
}
Future<FirebaseUser> signInFacebook() => _auth_repository.signInFacebook();
signOutFacebook(){
_auth_repository.signOutFacebook();
}
final _cloudFirestoreRepository = CloudFireStoreRepository();
void updateUserData(User user) => _cloudFirestoreRepository.updateUserDataFirestore(user);
#override
// TODO: implement initialState
get initialState => null;
#override
Stream mapEventToState(event) {
// TODO: implement mapEventToState
return null;
}
}
main.dart
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIOverlays([]);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ManagmentStorage managmentStorage = ManagmentStorage();
#override
void initState() {
managmentStorage.createStragoe();
super.initState();
}
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CameraBloc>(create: (BuildContext context) => CameraBloc()),
BlocProvider<UserBloc>(create: (BuildContext context) => UserBloc()),
],
child: MaterialApp(
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.dark,
debugShowCheckedModeBanner: false,
home: SignInScreen(), //ProfileUserHome(), //
routes: <String, WidgetBuilder>{
'/adddocument': (BuildContext context) => ScreenAddPhoto(),
'/screenimagedetail': (BuildContext context) => ScreenImageDetail()
},
),
);
}
}
screen 1:
class ProfileUser extends StatelessWidget {
UserBloc userBloc;
#override
Widget build(BuildContext context) {
userBloc = BlocProvider.of<UserBloc>(context);
return StreamBuilder(
stream: userBloc.auhtStatus,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Center(
child: SpinKitSquareCircle(color: Colors.white),
);
break;
case ConnectionState.waiting:
return Center(
child: SpinKitSquareCircle(color: Colors.white),
);
break;
case ConnectionState.active:
return showPofileData(snapshot);
break;
case ConnectionState.done:
return showPofileData(snapshot);
break;
default:
}
},
);
}
Widget showPofileData(AsyncSnapshot snapshot) {
if (!snapshot.hasData || snapshot.hasError) {
return Stack(
children: <Widget>[
ListView(
children: <Widget>[Text("Usuario no logeado. Haz login")],
),
],
);
} else {
var user = User(
uid: snapshot.data.uid,
name: snapshot.data.displayName,
email: snapshot.data.email,
photoURL: snapshot.data.photoUrl,
);
return Stack(
children: <Widget>[
UserBody(),
Header(
user: user,
)
],
);
}
}
}
screen2:
class ProfileUserHome extends StatelessWidget {
UserBloc userBloc;
#override
Widget build(BuildContext context) {
userBloc = BlocProvider.of<UserBloc>(context);
return Scaffold(
body: StreamBuilder(
stream: userBloc.auhtStatus,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Center(
child: SpinKitSquareCircle(color: Colors.white),
);
break;
case ConnectionState.waiting:
return Center(
child: SpinKitSquareCircle(color: Colors.white),
);
break;
case ConnectionState.active:
return profileuserinfo(snapshot);
break;
case ConnectionState.done:
return profileuserinfo(snapshot);
break;
default:
}
},
),
);
}
Widget profileuserinfo(AsyncSnapshot snapshot) {
if (!snapshot.hasData || snapshot.hasError) {
return Center(child: Text("Usuario no logeado. Haz login"));
} else {
var user = User(
uid: snapshot.data.uid,
name: snapshot.data.displayName,
email: snapshot.data.email,
photoURL: snapshot.data.photoUrl,
);
return ListView(children: <Widget>[
HeaderProfileUserHome(user: user)
]);
}
}
}
BottomNavigationBar:
class NavBarUser extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _NavBarUser();
}
}
class _NavBarUser extends State<NavBarUser> {
int indexTap = 0;
#override
Widget build(BuildContext context) {
PageController controller = PageController(initialPage: 0, keepPage: true);
void onTapTapped(int index) {
setState(() {
indexTap = index;
controller.animateToPage(index,
duration: Duration(milliseconds: 200), curve: Curves.ease);
});
}
void pageChanged(int index) {
setState(() {
indexTap = index;
});
}
final pageView = PageView(
controller: controller,
onPageChanged: (index) {
pageChanged(index);
},
children: <Widget>[UserHome(), ProfileUserHome()],
);
return MultiBlocProvider(
providers: [
BlocProvider<CameraBloc>(create: (BuildContext context) => CameraBloc()),
BlocProvider<UserBloc>(create: ( BuildContext context) => UserBloc()),
],
child: Scaffold(
body: pageView,
bottomNavigationBar: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.black,
primaryColor: Color.fromARGB(230, 226, 80, 116)),
child: BottomNavigationBar(
onTap: (index) {
onTapTapped(index);
},
currentIndex: indexTap,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("")),
BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text("")),
],
),
),
),
);
}
}
images of login succesful:
images screen 1
images screen2 profile and here is the problem :c
on the profile screen nothing appears to me and when I return home neither
You are doing it the wrong way, you are using the flutter_bloc library so:
You need to specify events and states.
you need to dispatch events to Bloc
handle events in mapEventToState
yield states
#override
Stream mapEventToState(event) {
// TODO: implement mapEventToState
return null;
}
And finally, listen to states using BlocBuilder widget by the flutter_bloc library instead of StreamBuilder
There is a well-written documentation&tutorial for the library you can check https://bloclibrary.dev/#/gettingstarted
Also you are providing BLOCS twice, in main.dart and NavBarUser widget.
Therefore remove it from NavBarUser
MultiBlocProvider(
providers: [
BlocProvider<CameraBloc>(create: (BuildContext context) => CameraBloc()),
BlocProvider<UserBloc>(create: ( BuildContext context) => UserBloc()),
],
i want to have switch widget in settings page. i can figured switching theme using switch widget, but this is too complicated
my main_page:
import 'package:intl/intl.dart';
Column(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Text("Summary"),
),
SizedBox(height: 45),
_time24(snapshot), //show this if switch is off
_time12(snapshot), //show this if switch is on
],
)
Widget _time12(AsyncSnapshot snapshot){
return Column(
children: <Widget>[
_time(Icons.home, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.home)))),
_time(Icons.work, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.work)))),
_time(Icons.restaurant, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.restaurant)))),
]);
}
Widget _time24(AsyncSnapshot snapshot){
return Column(
children: <Widget>[
_time(Icons.home, Text(snapshot.data.home)),
_time(Icons.work, Text(snapshot.data.work)),
_time(Icons.restaurant, Text(snapshot.data.restaurant)),
]);
}
Thank you for your time :)
You can copy paste run full code below
You can await Navigator.push then call setState()
code snippet
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => Setting()),
);
setState(() {});
}
...
else if (snapshot.hasData) {
if (is24) {
return _time24(snapshot);
} else {
return _time12(snapshot);
}
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class Payload {
String home;
String work;
String restaurant;
Payload({this.home, this.work, this.restaurant});
}
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 _counter = 0;
Future _future;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget _time12(AsyncSnapshot snapshot) {
var a = DateFormat("HH:mm").parse(snapshot.data.home);
print("a ${a}");
return Column(children: <Widget>[
_time(
Icons.home,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.home)))),
_time(
Icons.work,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.work)))),
_time(
Icons.restaurant,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.restaurant)))),
]);
}
Widget _time24(AsyncSnapshot snapshot) {
return Column(children: <Widget>[
_time(Icons.home, Text(snapshot.data.home)),
_time(Icons.work, Text(snapshot.data.work)),
_time(Icons.restaurant, Text(snapshot.data.restaurant)),
]);
}
Widget _time(IconData iconData, Text _text) {
return ListTile(
leading: Icon(iconData),
title: _text,
);
}
Future<Payload> getData() {
print("getData");
return Future.value(
Payload(home: "18:00", restaurant: "13:00", work: "08:00"));
}
#override
void initState() {
// TODO: implement initState
_future = getData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<Payload>(
future: _future,
builder: (BuildContext context, AsyncSnapshot<Payload> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...');
case ConnectionState.done:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else if (snapshot.hasData) {
if (is24) {
return _time24(snapshot);
} else {
return _time12(snapshot);
}
}
}
return null; // unreachable
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => Setting()),
);
setState(() {});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
bool is24 = true;
class Setting extends StatefulWidget {
#override
_SettingState createState() => _SettingState();
}
class _SettingState extends State<Setting> {
void _changed(value) {
setState(() {
is24 = value;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Container(
child: Switch(
value: is24,
onChanged: _changed,
)));
}
}
I'm new to using flutter framework and I want to pass call logs to my app so I can print them. How do I do that?
class _QuoteListState extends State<QuoteList> {
Future<bool>_callLogs() async {
Iterable<CallLogEntry> entries = await CallLog.get();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
title: Text('Calls Log'),
centerTitle: true,
backgroundColor: Colors.purple,
),
body: ListView.builder(
itemCount: entries.length,
itemBuilder: (context, index){
}
)
);
you have to fetch data from async function and set to local variables so it is good to call setState inside the _callLogs as and make your widget stateful and write as below
import 'package:call_log/call_log.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: LogsList(),
));
}
class LogsList extends StatefulWidget {
#override
_LogsListState createState() => _LogsListState();
}
class _LogsListState extends State<LogsList> {
#override
void initState() {
super.initState();
_callLogs();
}
Future<void> _callLogs() async {
Iterable<CallLogEntry> entries = await CallLog.get();
List _data = entries.map((data) => data).toList();
setState(() {
_entries = _data;
});
}
List<CallLogEntry> _entries = [];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
title: Text('Calls Log'),
centerTitle: true,
backgroundColor: Colors.purple,
),
body: ListView.builder(
itemCount: _entries.length,
itemBuilder: (context, i) {
final CallLogEntry callLogEntry = _entries[i];
return ListTile(
title: Text(callLogEntry.formattedNumber),
subtitle: Text(callLogEntry.duration.toString()),
);
}),
);
}
}
make sure to add this to your project Android Manifest:
You can use Future Builder to achieve so.
Following minimal code simulate you conditions, so you can implement in your own code.
import 'package:flutter/material.dart';
class DeleteWidget extends StatefulWidget {
const DeleteWidget({Key key}) : super(key: key);
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class _DeleteWidgetState extends State<DeleteWidget> {
#override
void initState() {
super.initState();
}
List<int> _data;
Future<bool> _callLogs() async {
await Future.delayed(Duration(seconds: 4));
_data = [6, 5, 4, 3, 2, 1];
return true;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: FutureBuilder(
future: _callLogs(),
builder: (context, snapshot) {
if (!snapshot.hasError) {
if (snapshot.data == true) {
return ListView.builder(
itemCount: _data.length,
itemBuilder: (context, index) {
return Text(_data[index].toString());
},
);
} else {
return Text("Whatever when it returns false");
}
} else {
return Text("Something wents wrong");
}
},
),
),
),
);
}
}
I have created a Flutter application with a list. On tap of an item, I am opening detail of that item.
The problem is whenever I come back from the detail screen, the list screen is reloaded. I don't want to reload the list every time.
I have used BloC architecture in this.
Below are the code snippets. Please suggest.
Thank You.
Main
void main() {
final userRepository = UserRepository();
ApiClient apiClient = ApiClient(httpClient: http.Client());
runApp(BlocProvider<AuthenticationBloc>(
builder: (context) {
return AuthenticationBloc(
userRepository: userRepository, apiClient: apiClient)
..dispatch(AppStarted());
},
child: MyApp(userRepository: userRepository),
));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
final UserRepository userRepository;
MyApp({Key key, #required this.userRepository}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
bloc: BlocProvider.of<AuthenticationBloc>(context),
builder: (context, state) {
if (state is AuthenticationUninitialized) {
return SplashPage();
}
if (state is AuthenticationAuthenticated) {
return HomePage(userRepository: userRepository);
}
if (state is AuthenticationUnauthenticated) {
return LoginPage(userRepository: userRepository);
}
if (state is AuthenticationLoading) {
return LoadingIndicator();
}
return null;
},
),
);
}
}
List Screen
class HomePage extends StatelessWidget {
UserRepository userRepository;
HomePage({#required this.userRepository}) : super();
#override
Widget build(BuildContext context) {
ApiClient apiClient = ApiClient(httpClient: http.Client());
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
drawer: AppDrawer(userRepository),
body: BlocProvider(
builder: (context) {
return HomeBloc(apiClient);
},
child: _HomeContent(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
backgroundColor: Colors.amberAccent,
),
);
}
}
class _HomeContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
final HomeBloc homeBloc = BlocProvider.of<HomeBloc>(context);
homeBloc.dispatch(FetchMovieList());
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
if (state is MovieListLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
if (state is MovieListLoaded) {
List<Movie> topRatedMovies = state.movieList;
return new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: Card(
child: Row(
children: <Widget>[
Image.network(ApiClient.IMAGE_BASE_URL +
topRatedMovies[index].poster_path),
Column(
children: <Widget>[
Text(topRatedMovies[index].title),
],
)
],
),
),
onTap: () {
_onListItemTapped(topRatedMovies[index].id, context);
},
);
},
itemCount: topRatedMovies.length,
);
}
if (state is MovieListError) {
return Center(
child: Text('Error in calling API'),
);
}
return Center(child: Text('Employee data not found'));
},
);
}
void _onListItemTapped(int movieId, BuildContext context) {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => MovieDetailPage(
movieId: movieId,
)));
}
}
At anytime your build method needs to be ready for multiple build calls. If build calls are causing problem then something is probably wrong. It would be a better idea to fetch the data outside the build method to prevent unnecessary API calls.
For example you can create a Stateful Widget and in initState method you can fetch the data. After that, build method is called to prepare UI with the data. You can use a Future Builder to show progress and update UI when the data is fetched.
Example:
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Future _future;
Future getData() async {
// Fetch data
}
#override
void initState() {
super.initState();
_future = getData();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _future,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.data.hasErrors) {
return Text('Error: ${snapshot.data.errors}');
} else {
// Data is fetched, build UI
return ListTile();
}
}
});
}
}