I wanted to show appbar logo for all the pages from a json file. Now the issue is if I use Futurebuilder then in every page load, appbar logo awaits before showing. I tried to use shared preference but having issues. Maybe I am doing it wrong. I am a newbie in flutter. If possible please give the answer in simple way so I can understand. Or anyone can help me by creating that part for appbar. That will be helpful.
import 'dart:convert';
import 'package:models/app_logo.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import './home_screen.dart';
import './login_screen.dart';
import '../constants.dart';
import '../screens/courses_screen.dart';
import '../screens/my_courses_screen.dart';
import '../screens/my_wishlist_screen.dart';
import '../screens/account_screen.dart';
import '../widgets/filter_widget.dart';
import '../providers/auth.dart';
import 'package:http/http.dart' as http;
class TabsScreen extends StatefulWidget {
@override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
List<Widget> _pages = [
HomeScreen(),
LoginScreen(),
LoginScreen(),
LoginScreen(),
];
var _isInit = true;
var _isLoading = false;
int _selectedPageIndex = 0;
bool _isSearching = false;
final searchController = TextEditingController();
Future<AppLogo> futureLogo;
Future<AppLogo> fetchMyLogo() async {
var url = BASE_URL + '/app_logo';
try {
final response = await http.get(url);
print(response.body);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
print(response.body);
return AppLogo.fromJson(jsonDecode(response.body));
}
// print(extractedData);
} catch (error) {
throw (error);
}
}
@override
void initState() {
super.initState();
this.fetchMyLogo();
// Provider.of<Auth>(context).tryAutoLogin().then((_) {});
}
@override
void didChangeDependencies() {
if (_isInit) {
setState(() {
_isLoading = true;
});
final _isAuth = Provider.of<Auth>(context, listen: false).isAuth;
if (_isAuth) {
_pages = [
HomeScreen(),
MyCoursesScreen(),
MyWishlistScreen(),
AccountScreen(),
];
}
}
_isInit = false;
super.didChangeDependencies();
}
void _handleSubmitted(String value) {
final searchText = searchController.text;
if (searchText.isEmpty) {
return;
}
searchController.clear();
Navigator.of(context).pushNamed(
CoursesScreen.routeName,
arguments: {
'category_id': null,
'seacrh_query': searchText,
'type': CoursesPageData.Search,
},
);
// print(searchText);
}
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: kSecondaryColor, //change your color here
),
title: !_isSearching
? FutureBuilder<AppLogo>(
future: fetchMyLogo(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: Container(),
);
} else {
if (snapshot.error != null) {
return Center(
child: Text("Error Occured"),
);
} else {
return Image.network(
snapshot.data.darkLogo,
fit: BoxFit.contain,
height: 27,
);
}
}
},
)
: TextFormField(
decoration: InputDecoration(
labelText: 'Search Here',
prefixIcon: Icon(
Icons.search,
color: Colors.grey,
),
),
controller: searchController,
onFieldSubmitted: _handleSubmitted,
),
backgroundColor: kBackgroundColor,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: kSecondaryColor,
),
onPressed: () {
setState(() {
_isSearching = !_isSearching;
});
}),
],
),
body: _pages[_selectedPageIndex],
floatingActionButton: FloatingActionButton(
onPressed: () => _showFilterModal(context),
child: Icon(Icons.filter_list),
backgroundColor: kDarkButtonBg,
),
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
items: [
BottomNavigationBarItem(
backgroundColor: kBackgroundColor,
icon: Icon(Icons.school),
title: Text('Course'),
),
BottomNavigationBarItem(
backgroundColor: kBackgroundColor,
icon: Icon(Icons.shopping_basket),
title: Text('My Course'),
),
BottomNavigationBarItem(
backgroundColor: kBackgroundColor,
icon: Icon(Icons.favorite_border),
title: Text('Wishlist'),
),
BottomNavigationBarItem(
backgroundColor: kBackgroundColor,
icon: Icon(Icons.account_circle),
title: Text('Account'),
),
],
backgroundColor: kBackgroundColor,
unselectedItemColor: kSecondaryColor,
selectedItemColor: kSelectItemColor,
currentIndex: _selectedPageIndex,
type: BottomNavigationBarType.fixed,
),
);
}
}
There are some ways you can do to reduce the load time of the logo in your AppBar. Since this AppBar is common between the tabs, you should only load it once and avoid loading again every time the tab is changed.
First is to use StreamBuilder
instead of FutureBuilder
to reduce the number of loads.
// Create a StreamController
final _controller = StreamController<AppLogo>();
// Run fetchMyLogo() in your initState() like in your code
// In your fetchMyLogo(), add the result to the stream
fetchMyLogo() async {
// ... other lines
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
var logo = AppLogo.fromJson(jsonDecode(response.body));
_controller.add(logo);
}
// Then, listen to this logo in your StreamBuilder
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// ... other lines
title: !_isSearching
? StreamBuilder<AppLogo>(
stream: _controller.stream,
builder: (context, snapshot) {
// ... other lines
Second is to use the cached_network_image instead of Image.network
, so that your logo image is cached, which reduce load time for network images.
return CachedNetworkImage(
imageUrl: snapshot.data.darkLogo,
fit: BoxFit.contain,
height: 27,
);
Small note: For each of the page in your _pages
list, if you want the page to persist (not reload after every tab change), you can use the AutomaticKeepAliveClientMixin
to persist the state of each page, or use IndexedStack
like:
// The IndexedStack will load all pages initially
body: IndexedStack(
children: _pages,
index: _selectedPageIndex,
),
// ... other lines