使用 json 文件为 flutter 提供 appbar 徽标。导航时面临加载问题
Using json file to give appbar logo for flutter. Facing Loading issue while navigation
我想为 json 文件中的所有页面显示应用栏徽标。现在的问题是如果我使用 Futurebuilder
然后在每次页面加载时,appbar 标志在显示之前等待。我尝试使用共享偏好但遇到问题。也许我做错了。我是颤振的新手。如果可能,请用简单的方式给出答案,以便我理解。或者任何人都可以通过为 appbar 创建该部分来帮助我。这会很有帮助。
Here is my json file for logo
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,
),
);
}
}
您可以通过一些方法来减少徽标在 AppBar 中的加载时间。由于此 AppBar 在选项卡之间是通用的,因此您应该只加载一次,避免每次更改选项卡时都重新加载。
首先是用StreamBuilder
代替FutureBuilder
来减少加载次数
// 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
其次是使用 cached_network_image 而不是 Image.network
,以便缓存您的徽标图像,从而减少网络图像的加载时间。
return CachedNetworkImage(
imageUrl: snapshot.data.darkLogo,
fit: BoxFit.contain,
height: 27,
);
小提示:对于你_pages
列表中的每个页面,如果你想让页面持久化(而不是在每次标签更改后重新加载),你可以使用 AutomaticKeepAliveClientMixin
来保持每个页面的状态,或者使用 IndexedStack
像:
// The IndexedStack will load all pages initially
body: IndexedStack(
children: _pages,
index: _selectedPageIndex,
),
// ... other lines
我想为 json 文件中的所有页面显示应用栏徽标。现在的问题是如果我使用 Futurebuilder 然后在每次页面加载时,appbar 标志在显示之前等待。我尝试使用共享偏好但遇到问题。也许我做错了。我是颤振的新手。如果可能,请用简单的方式给出答案,以便我理解。或者任何人都可以通过为 appbar 创建该部分来帮助我。这会很有帮助。
Here is my json file for logo
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,
),
);
}
}
您可以通过一些方法来减少徽标在 AppBar 中的加载时间。由于此 AppBar 在选项卡之间是通用的,因此您应该只加载一次,避免每次更改选项卡时都重新加载。
首先是用StreamBuilder
代替FutureBuilder
来减少加载次数
// 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
其次是使用 cached_network_image 而不是 Image.network
,以便缓存您的徽标图像,从而减少网络图像的加载时间。
return CachedNetworkImage(
imageUrl: snapshot.data.darkLogo,
fit: BoxFit.contain,
height: 27,
);
小提示:对于你_pages
列表中的每个页面,如果你想让页面持久化(而不是在每次标签更改后重新加载),你可以使用 AutomaticKeepAliveClientMixin
来保持每个页面的状态,或者使用 IndexedStack
像:
// The IndexedStack will load all pages initially
body: IndexedStack(
children: _pages,
index: _selectedPageIndex,
),
// ... other lines