I want to get data from Provider based on argument and populate that data into each BottomNavigationBarItem
On this screen I get "meetingId" argument and use that on Provider to populate a model class with data.
class MeetingDetailScreen extends StatefulWidget {
final meetingId;
const MeetingDetailScreen({Key? key, this.meetingId}) : super(key: key);
static const routeName = '/meeting-detail';
#override
_MeetingDetailScreenState createState() => _MeetingDetailScreenState();
}
class _MeetingDetailScreenState extends State<MeetingDetailScreen> {
String meetingId = '';
var _isInit = true;
var _isLoading = false;
final List<Widget> _pages = [
DetailsScreen(),
DocumentsScreen(),
AgendaScreen(),
MinutesScreen(),
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
void initState() {
super.initState();
}
#override
void didChangeDependencies() {
if (_isInit) {
setState(() {
_isLoading = true;
});
//This is provider
Provider.of<MeetingDetails>(context)
.fetchAndSetMeetingDetails(widget.meetingId)
.then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = false;
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 248, 1),
appBar: AppBar(
backgroundColor: Colors.white,
centerTitle: true,
iconTheme: const IconThemeData(color: Color.fromRGBO(150, 188, 51, 1)),
title: Image.asset(
"assets/images/appbar-logo.png",
fit: BoxFit.contain,
height: 50,
),
),
drawer: const MainDrawer(),
body: _pages[_selectedPageIndex],
//body: getPage(_selectedPageIndex),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.info_outline),
label: 'Details',
),
BottomNavigationBarItem(
icon: Icon(Icons.file_download),
label: 'Documents',
),
BottomNavigationBarItem(
icon: Icon(Icons.format_list_bulleted_outlined),
label: 'Agenda',
),
BottomNavigationBarItem(
icon: Icon(Icons.note_alt_outlined),
label: 'Minutes',
),
],
currentIndex: _selectedPageIndex,
selectedItemColor: Colors.amber[800],
onTap: _selectPage,
),
);
}
}
This is the BottomNavigationBarItem screen "DetailsScreen()"
class DetailsScreen extends StatelessWidget {
static const routeName = '/meeting-detail';
const DetailsScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final meetingDetailsList = Provider.of<MeetingDetails>(context);
final meetingDetails = meetingDetailsList.items.toList()[0];
return Center(
child: Text(
meetingDetails.event_name,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
);
}
}
Problem is Provider updates data from API and it takes a moment. when I try to use Provider data on BottomNavigationBar it throws an error because provider data is null when loading the screen. Is there any way to delay the BottomNavigationBarItem until Provider populates data?
Thank you
Problem is I wasn't using builder with ChangeNotifierProvider.
Inside widget I used following builder code and problem solved.
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: meeting.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
value: meeting[i],
child:
buildDetailRow(context, 'Meeting Name', meeting[i].event_name),
),
),
],
);
Related
My Flutter app uses a BottomNavigationBar wrapped in an AnimatedContainer. When the animation takes place (activated by scrolling the list) a RenderFlex overflow error occurs. I can't work out what is causing this to happen.
I've stripped down the project to bare bones code in the hope that someone could try it out and identify the issue.
The main class:
class TestMain extends StatefulWidget {
const TestMain({Key? key}) : super(key: key);
#override
State<TestMain> createState() => _TestMain();
}
class BottomNavBarItemData {
String label;Icon icon;Widget screen;
BottomNavBarItemData({required this.label,required this.icon,required this.screen});
}
late ScrollController mainScrollController;
class _TestMain extends State<TestMain> {
int _selectedIndex = 0;
bool _isVisible = true;
#override
void initState() {
_isVisible = true;
mainScrollController = ScrollController();
mainScrollController.addListener(() {
if (mainScrollController.position.userScrollDirection == ScrollDirection.reverse) {
setState(() {
_isVisible = false;
});
}
if (mainScrollController.position.userScrollDirection == ScrollDirection.forward) {
setState(() {
_isVisible = true;
});
}
});
super.initState();
}
final List<BottomNavBarItemData> screens = [
BottomNavBarItemData(
icon: const Icon(Icons.home, size: 25.0, color: Colors.red),
label: 'Page1',
screen: const Screen1(),
),
BottomNavBarItemData(
icon: const Icon(Icons.home, size: 25.0, color: Colors.red),
label: 'Page2',
screen: const Screen2(),
),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
extendBody: true,
body: SafeArea(
child: IndexedStack(
index: _selectedIndex,
children: [
...screens.map((e) => e.screen).toList(),
],
),
),
bottomNavigationBar: AnimatedContainer(
duration: const Duration(milliseconds: 400),
height: _isVisible ? 70 : 0.0,
child: SizedBox(
child: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.orange,
currentIndex: _selectedIndex,
selectedIconTheme: IconThemeData(color: Colors.white),
selectedItemColor: Colors.white,
selectedFontSize: 14,
unselectedFontSize: 14,
unselectedIconTheme: const IconThemeData(
color: Colors.lightBlueAccent,
),
unselectedItemColor: Colors.lightBlueAccent,
onTap: _onItemTapped,
items: screens.map((e) => BottomNavigationBarItem(
label: e.label,
icon: e.icon,
),
).toList(),
),
),
),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
}
And the two screens called by the main class:
class Screen1 extends StatefulWidget {
const Screen1({Key? key}) : super(key: key);
#override
State<Screen1> createState() => _Screen1();
}
class _Screen1 extends State<Screen1> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: SingleChildScrollView(
controller: mainScrollController,
physics: const AlwaysScrollableScrollPhysics(),
child: Column(children: [
Container(height: 150, color: Colors.blue),
Container(height: 150, color: Colors.white),
Container(height: 150, color: Colors.blue),
Container(height: 150, color: Colors.white),
Container(height: 150, color: Colors.blue),
Container(height: 150, color: Colors.white),
],),
),
);
}
}
class Screen2 extends StatefulWidget {
const Screen2({Key? key}) : super(key: key);
#override
State<Screen2> createState() => _Screen2();
}
class _Screen2 extends State<Screen2> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
);
}
}
Container gets overflow because the inner item space is greater to the animation size, like on 35 it will show the overflow. You can use different animation, but it will be little difference.
You can use SizeTransition for this case
class _TestMain extends State<TestMain> with SingleTickerProviderStateMixin {
int _selectedIndex = 0;
bool _isVisible = true;
late final AnimationController _controller = AnimationController(
duration: const Duration(milliseconds: 400),
vsync: this,
)..forward();
late final Animation<double> _animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
);
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
void initState() {
_isVisible = true;
mainScrollController = ScrollController();
mainScrollController.addListener(() {
if (mainScrollController.position.userScrollDirection ==
ScrollDirection.reverse) {
_controller.reverse();
setState(() {
_isVisible = false;
});
}
if (mainScrollController.position.userScrollDirection ==
ScrollDirection.forward) {
_controller.forward();
setState(() {
_isVisible = true;
});
}
});
super.initState();
}
final List<BottomNavBarItemData> screens = [
BottomNavBarItemData(
icon: const Icon(Icons.home, size: 25.0, color: Colors.red),
label: 'Page1',
screen: const Screen1(),
),
BottomNavBarItemData(
icon: const Icon(Icons.home, size: 25.0, color: Colors.red),
label: 'Page2',
screen: const Screen2(),
),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
extendBody: true,
body: SafeArea(
child: IndexedStack(
index: _selectedIndex,
children: [
...screens.map((e) => e.screen).toList(),
],
),
),
bottomNavigationBar: SizeTransition(
sizeFactor: _animation,
child: SizedBox(
child: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.orange,
currentIndex: _selectedIndex,
selectedIconTheme: IconThemeData(color: Colors.white),
selectedItemColor: Colors.white,
selectedFontSize: 14,
unselectedFontSize: 14,
unselectedIconTheme: const IconThemeData(
color: Colors.lightBlueAccent,
),
unselectedItemColor: Colors.lightBlueAccent,
onTap: _onItemTapped,
items: screens
.map(
(e) => BottomNavigationBarItem(
label: e.label,
icon: e.icon,
),
)
.toList(),
),
),
),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
}
or
bottomNavigationBar: AnimatedScale(
duration: const Duration(milliseconds: 400),
scale: _isVisible ? 1 : 0.0,
alignment: Alignment.bottomCenter,
I have 2 dart files home.dart and video_list.dart.
I have a PopupMenuButton and a BottomNavigationBar in home.dart. video_list.dart is shown in the "body:" of home.dart.
Each time the value changes in PopupMenuButton in home.dart, I want to call a function in video_list.dart (_loadPlaylist()) so that the ListView in video_list.dart is refreshed.
How is this possible? I am a newbie.
home.dart
import 'package:crt/models/subject.dart';
import 'package:crt/pages/faq.dart';
import 'package:crt/pages/quiz.dart';
import 'package:crt/pages/video_list.dart';
import 'package:crt/pages/resource_list.dart';
import 'package:flutter/material.dart';
import 'package:crt/data/subjects.dart';
class Home extends StatefulWidget {
Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
//GlobalKey<_VideoListState> _key = GlobalKey<_VideoListState>();
Subject _selectedSubject = subjects[0];
int _currentIndex = 0;
List<StatefulWidget> pages = [];
#override
void initState() {
super.initState();
_setPages();
}
void _setPages() {
pages = [
VideoList(_selectedSubject),
ResourceList(_selectedSubject),
Quiz(_selectedSubject),
Faq(_selectedSubject),
];
}
IndexedStack _loadPage() {
return IndexedStack(
index: _currentIndex,
children: pages,
);
}
void _changeSubject(Subject subject) {
setState(() {
_selectedSubject = subject;
_setPages();
if (_selectedSubject.name == 'English') {
// I want to call _loadPlaylist function defined in video_list.dart
// VideoList()._loadPlaylist;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_selectedSubject.name),
elevation: 0,
actions: [
PopupMenuButton<Subject>(
onSelected: _changeSubject,
itemBuilder: (BuildContext context) {
return subjects.map((Subject subject) {
return PopupMenuItem<Subject>(
value: subject,
child: Text(subject.name),
);
}).toList();
},
)
],
),
body: _loadPage(),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
iconSize: 30,
selectedFontSize: 14,
unselectedFontSize: 14,
currentIndex: _currentIndex,
onTap: (index) => setState(() {
_currentIndex = index;
}),
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.video_library),
label: 'Videos',
),
BottomNavigationBarItem(
icon: Icon(Icons.library_books),
label: 'Resources',
),
BottomNavigationBarItem(
icon: Icon(Icons.quiz),
label: 'Quiz',
),
BottomNavigationBarItem(
icon: Icon(Icons.question_answer),
label: 'FAQ',
),
],
),
);
}
}
video_list.dart
import 'package:crt/models/subject.dart';
import 'package:flutter/material.dart';
import 'package:crt/models/playlist.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:crt/utilities/services.dart';
class VideoList extends StatefulWidget {
VideoList(Subject this.selectedSubject, {Key? key}) : super(key: key);
final Subject selectedSubject;
#override
State<VideoList> createState() => _VideoListState();
}
class _VideoListState extends State<VideoList> {
late Playlist _playlist;
bool _isLoading = true;
late String _name;
#override
void initState() {
super.initState();
_playlist = Playlist();
_playlist.items = List.empty(growable: true);
_loadPlaylist();
}
//I want to call this function from home.dart
_loadPlaylist() async {
String playlistId = widget.selectedSubject.playlistId;
Playlist playlist = await Services.getPlaylist(playlistId: playlistId);
_playlist.items?.addAll(playlist.items as Iterable<Item>);
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: ListView.builder(
itemCount: _playlist.items?.length,
itemBuilder: ((context, index) {
Item item = _playlist.items![index];
return Card(
child: InkWell(
onTap: () async {
// Navigator.push(context, MaterialPageRoute(builder: (context) {
// return VideoPlayer(
// item: item,
// );
// }));
},
child: Row(children: [
Padding(
padding: const EdgeInsets.all(5.0),
child: CachedNetworkImage(
imageUrl:
item.snippet.thumbnails.thumbnailsDefault.url),
),
SizedBox(
width: 10.0,
),
Flexible(
child: Text(
item.snippet.title,
style: TextStyle(
fontSize: 18.0,
),
)),
const SizedBox(
width: 20.0,
),
]),
),
);
})),
);
}
}
Just move the _loadPlayList() function to Home and pass it down to VideoList widget.
class VideoList extends StatefulWidget {
VideoList(Subject this.selectedSubject, {Key? key, this.loadPlayList})
: super(key: key);
final Subject selectedSubject;
final Future<Function> loadPlayList;
#override
State<VideoList> createState() => _VideoListState();
}
The whole code - home.dart
import 'package:crt/models/subject.dart';
import 'package:crt/pages/faq.dart';
import 'package:crt/pages/quiz.dart';
import 'package:crt/pages/video_list.dart';
import 'package:crt/pages/resource_list.dart';
import 'package:flutter/material.dart';
import 'package:crt/data/subjects.dart';
class Home extends StatefulWidget {
Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
//GlobalKey<_VideoListState> _key = GlobalKey<_VideoListState>();
Subject _selectedSubject = subjects[0];
int _currentIndex = 0;
List<StatefulWidget> pages = [];
#override
void initState() {
super.initState();
_setPages();
}
void _setPages() {
pages = [
VideoList(_selectedSubject, loadPlayList()),
ResourceList(_selectedSubject),
Quiz(_selectedSubject),
Faq(_selectedSubject),
];
}
loadPlaylist() async {
String playlistId = widget.selectedSubject.playlistId;
Playlist playlist = await Services.getPlaylist(playlistId: playlistId);
_playlist.items?.addAll(playlist.items as Iterable<Item>);
}
IndexedStack _loadPage() {
return IndexedStack(
index: _currentIndex,
children: pages,
);
}
void _changeSubject(Subject subject) {
setState(() {
_selectedSubject = subject;
_setPages();
if (_selectedSubject.name == 'English') {
this.loadPlaylist();
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_selectedSubject.name),
elevation: 0,
actions: [
PopupMenuButton<Subject>(
onSelected: _changeSubject,
itemBuilder: (BuildContext context) {
return subjects.map((Subject subject) {
return PopupMenuItem<Subject>(
value: subject,
child: Text(subject.name),
);
}).toList();
},
)
],
),
body: _loadPage(),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
iconSize: 30,
selectedFontSize: 14,
unselectedFontSize: 14,
currentIndex: _currentIndex,
onTap: (index) => setState(() {
_currentIndex = index;
}),
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.video_library),
label: 'Videos',
),
BottomNavigationBarItem(
icon: Icon(Icons.library_books),
label: 'Resources',
),
BottomNavigationBarItem(
icon: Icon(Icons.quiz),
label: 'Quiz',
),
BottomNavigationBarItem(
icon: Icon(Icons.question_answer),
label: 'FAQ',
),
],
),
);
}
}
video_list.dart
import 'package:crt/models/subject.dart';
import 'package:flutter/material.dart';
import 'package:crt/models/playlist.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:crt/utilities/services.dart';
class VideoList extends StatefulWidget {
VideoList(Subject this.selectedSubject, this.loadPlayList, {Key? key})
: super(key: key);
final Subject selectedSubject;
final Future<Function> loadPlayList;
#override
State<VideoList> createState() => _VideoListState();
}
class _VideoListState extends State<VideoList> {
late Playlist _playlist;
bool _isLoading = true;
late String _name;
#override
void initState() {
super.initState();
_playlist = Playlist();
_playlist.items = List.empty(growable: true);
widget.loadPlaylist().then(() {
setState(() {
_isLoading = false;
});
};
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: ListView.builder(
itemCount: _playlist.items?.length,
itemBuilder: ((context, index) {
Item item = _playlist.items![index];
return Card(
child: InkWell(
onTap: () async {
// Navigator.push(context, MaterialPageRoute(builder: (context) {
// return VideoPlayer(
// item: item,
// );
// }));
},
child: Row(children: [
Padding(
padding: const EdgeInsets.all(5.0),
child: CachedNetworkImage(
imageUrl:
item.snippet.thumbnails.thumbnailsDefault.url),
),
SizedBox(
width: 10.0,
),
Flexible(
child: Text(
item.snippet.title,
style: TextStyle(
fontSize: 18.0,
),
)),
const SizedBox(
width: 20.0,
),
]),
),
);
})),
);
}
}
The app has a BottomNavigationBar (always visible) and when I added the search tab this error apears (Search page was writed following a tutorial from youtube). I'm not sure about how can manage the state when I leave the search page or how to restart it.
I realice error apears when no search has been performed
Can someone help to solve this error?
Here is my code
home
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static final List<Widget> _widgetOptions = <Widget>[
const Text(
'Index 1: Home',
style: optionStyle,
),
const Text(
'Index 1: Events',
style: optionStyle,
),
SearchScreen(),
const Text(
'Index 3: Messages',
style: optionStyle,
),
ProfilePage(uid: FirebaseAuth.instance.currentUser!.uid)
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: null,
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: mobileBackgroundColor,
),
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today),
label: 'Events',
backgroundColor: mobileBackgroundColor,
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
backgroundColor: mobileBackgroundColor,
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.chat_bubble_2),
label: 'Messages',
backgroundColor: mobileBackgroundColor,
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
backgroundColor: mobileBackgroundColor,
),
],
unselectedItemColor: Colors.black12,
selectedItemColor: Colors.deepOrangeAccent,
currentIndex: _selectedIndex,
onTap: _onItemTapped,
));
}
}
search
class Search extends StatefulWidget {
#override
_SearchState createState() => _SearchState();
}
class _SearchState extends State<Search> {
static const historyLenght = 5;
final List<String> _searchHistory = ['pink', 'blue'];
late List<String> filteredSearchHistory;
String selectedTerm = "Search";
List<String> filterSearchTerms({
#required String? filter,
}) {
if (filter != null && filter.isNotEmpty) {
return _searchHistory.reversed
.where((term) => term.startsWith(filter))
.toList();
} else {
return _searchHistory.reversed.toList();
}
}
void addSearchTerm(String term) {
if (_searchHistory.contains(term)) {
putSearchTermFirst(term);
return;
}
_searchHistory.add(term);
if (_searchHistory.length > historyLenght) {
_searchHistory.removeRange(0, _searchHistory.length - historyLenght);
}
filteredSearchHistory = filterSearchTerms(filter: null);
}
void deleteSearchTerm(String term) {
_searchHistory.removeWhere((element) => element == term);
filteredSearchHistory = filterSearchTerms(filter: null);
}
void putSearchTermFirst(String term) {
deleteSearchTerm(term);
addSearchTerm(term);
}
late FloatingSearchBarController controller;
#override
void initState() {
super.initState();
controller = FloatingSearchBarController();
filteredSearchHistory = filterSearchTerms(filter: null);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingSearchBar(
controller: controller,
body: FloatingSearchBarScrollNotifier(
child: SearchResultListView(
searchTerm: selectedTerm,
),
),
transition: CircularFloatingSearchBarTransition(),
physics: const BouncingScrollPhysics(),
title: Text(selectedTerm),
hint: 'Search ... ',
actions: [
FloatingSearchBarAction.searchToClear(),
],
onQueryChanged: (query) {
setState(() {
filteredSearchHistory = filterSearchTerms(filter: query);
});
},
onSubmitted: (query) {
setState(() {
addSearchTerm(query);
selectedTerm = query;
});
controller.close();
},
builder: (context, transition) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Material(
color: Colors.white,
elevation: 4,
child: Builder(builder: ((context) {
if (filteredSearchHistory.isEmpty &&
controller.query.isEmpty) {
return Container(
height: 56,
width: double.infinity,
alignment: Alignment.center,
child: const Text(
'Start searching',
maxLines: 1,
overflow: TextOverflow.ellipsis,
));
} else if (filteredSearchHistory.isEmpty) {
return ListTile(
title: Text(controller.query),
leading: const Icon(Icons.search),
onTap: () {
setState(() {
addSearchTerm(controller.query);
selectedTerm = controller.query;
});
controller.close();
},
);
} else {
return Column(
mainAxisSize: MainAxisSize.min,
children: filteredSearchHistory
.map(
(e) => ListTile(
title: Text(e,
maxLines: 1,
overflow: TextOverflow.ellipsis),
leading: const Icon(Icons.history),
trailing: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
setState(() {
deleteSearchTerm(e);
});
},
),
onTap: () {
setState(() {
putSearchTermFirst(e);
selectedTerm = e;
});
controller.close();
},
),
)
.toList());
}
}))),
);
},
),
);
}
}
class SearchResultListView extends StatelessWidget {
final String searchTerm;
const SearchResultListView({
Key? key,
required this.searchTerm,
}) : super(key: key);
#override
Widget build(BuildContext context) {
if (searchTerm == null) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [Icon(Icons.search), Text('data')],
),
);
}
final sBar = FloatingSearchBar.of(context);
return ListView(
padding: EdgeInsets.only(
top: sBar!.style.height + sBar.style.margins.vertical),
children: List.generate(
50,
(index) => ListTile(
title: Text('Hola $searchTerm'),
subtitle: Text(index.toString()),
)));
}
}
I face this problem when I want to add lists.dart in BottomNavigationBarItem. in bottom of scaffold it shows red underline- body: _children[_currentIndex],
but don't know, what is the actual problem. what does it mean by
The operator '[]' isn't defined for the type 'List'
?
this is the full code of main.dart
void main() {
runApp(Main());
}
class Main extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
late SharedPreferences sharedPreferences;
int _currentIndex = 0;
final List _children = [
Home(),
OrganizationData(),
Notifications(),
More(),
] as List;
void onTapped(int index) {
setState(() {
_currentIndex = index;
});
}
#override
void initState() {
super.initState();
checkLoginStatus();
}
checkLoginStatus() async {
sharedPreferences = await SharedPreferences.getInstance();
if (sharedPreferences.getString("token") == null) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext context) => LoginPage()),
(Route<dynamic> route) => false);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leadingWidth: 80,
toolbarHeight: 80,
titleSpacing: -15.0,
title: ListTile(
title: Text(
"FAYZULLAH",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
subtitle: Text(
"Jr. executive",
style: TextStyle(
fontSize: 12,
),
),
),
leading: GestureDetector(
onTap: () {},
child: Container(
padding: EdgeInsets.all(10.0),
margin: EdgeInsets.all(10.0),
decoration: new BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(50.0),
),
border: Border.all(
width: 1,
style: BorderStyle.solid,
color: Colors.black12,
),
image: DecorationImage(
image: AssetImage('assets/fozuda.png'),
),
),
),
),
actions: [
IconButton(
icon: Icon(
Icons.logout_rounded,
size: 20,
),
color: Colors.black,
onPressed: () {
sharedPreferences.clear();
sharedPreferences.commit();
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) => LoginPage()),
(Route<dynamic> route) => false);
},
),
],
backgroundColor: Colors.yellow,
),
bottomNavigationBar: BottomNavigationBar(
onTap: onTapped,
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
// backgroundColor: Colors.yellow,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
label: 'Lists',
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications),
label: 'Notification',
),
BottomNavigationBarItem(
icon: Icon(Icons.more_horiz_rounded),
label: 'More',
),
],
),
body: _children[_currentIndex],
);
}
}
and this is the lists.dart code
class OrganizationData extends StatefulWidget {
const OrganizationData({Key? key}) : super(key: key);
#override
_OrganizationDataState createState() => _OrganizationDataState();
}
class _OrganizationDataState extends State<OrganizationData> {
late Future<List> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = listData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<List>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
// print(snapshot.data);
return Text(snapshot.data!.fullName);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
);
}
}
Future<List> listData() async {
Dio dio = new Dio();
dio.options.headers['Content-Type'] = 'application/json';
final body = {'limit': 1, 'orderBy': 'idEmployee', 'orderType': 'DESC'};
final response = await dio.post(url, data: body);
if (response.statusCode == 200) {
return List.fromJson(response.data["data"]["data"][0]);
} else {
throw Exception('Failed!');
}
}
class List {
final int idEmployee;
final String fullName;
List({
required this.idEmployee,
required this.fullName,
});
factory List.fromJson(Map<String, dynamic> json) {
return List(
idEmployee: json['idEmployee'],
fullName: json['fullName'],
);
}
}
This is run console
Rename your List class in lists.dart in some other name and correct the name everywhere you used your List class.
Because there is a built in object named List
So, you cannot name your classes "List".
Naming your own classes "List" will override the built in List class. That's why dart tells that "The operator '[]' isn't defined for the type 'List'".
So, change your class name everywhere you used the class.
i need to set background color to selected BottomNavigationBarItem like this image
It's not possible to change the background of a selected item of the BottomNavigationBar because that doesn't follow the design guidelines.
If you still want to use it that way, following the example given in BottomNavigationBar class, here is a workaround:
final _selectedItemColor = Colors.white;
final _unselectedItemColor = Colors.white30;
final _selectedBgColor = Colors.indigo;
final _unselectedBgColor = Colors.blue;
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static const List<Widget> _widgetOptions = <Widget>[
Text(
'Index 0: Home',
style: optionStyle,
),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
Color _getBgColor(int index) =>
_selectedIndex == index ? _selectedBgColor : _unselectedBgColor;
Color _getItemColor(int index) =>
_selectedIndex == index ? _selectedItemColor : _unselectedItemColor;
Widget _buildIcon(IconData iconData, String text, int index) => Container(
width: double.infinity,
height: kBottomNavigationBarHeight,
child: Material(
color: _getBgColor(index),
child: InkWell(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(iconData),
Text(text,
style: TextStyle(fontSize: 12, color: _getItemColor(index))),
],
),
onTap: () => _onItemTapped(index),
),
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
selectedFontSize: 0,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: _buildIcon(Icons.home, 'Home', 0),
title: SizedBox.shrink(),
),
BottomNavigationBarItem(
icon: _buildIcon(Icons.business, 'Business', 1),
title: SizedBox.shrink(),
),
BottomNavigationBarItem(
icon: _buildIcon(Icons.school, 'School', 2),
title: SizedBox.shrink(),
),
],
currentIndex: _selectedIndex,
selectedItemColor: _selectedItemColor,
unselectedItemColor: _unselectedItemColor,
),
);
}
Result:
As far as I can see, you cannot change the color of a tab when using BottomNavigationBar. However, can I suggest to switch to TabBar? It would look something like this:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: 3);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: TabBarView(
controller: _tabController,
children: <Widget>[
Scaffold(appBar: AppBar(title: Text('First'))),
Scaffold(appBar: AppBar(title: Text('Second'))),
Scaffold(appBar: AppBar(title: Text('Third'))),
],
),
bottomNavigationBar: BottomAppBar(
child: Container(
color: Colors.blue,
child: TabBar(
indicator: BoxDecoration(color: Colors.redAccent),
tabs: <Widget>[
Tab(child: Text("Stuff", style: TextStyle(color: Colors.white))),
Tab(child: Text("Things", style: TextStyle(color: Colors.white))),
Tab(child: Text("Items", style: TextStyle(color: Colors.white))),
],
controller: _tabController,
),
),
),
);
}
}
Result:
This is a very minimal example, but it demonstrates the gist of it.
Here is example for changing background on click:
class _MyWidgetState extends State<MyWidget> {
IconData selectedItem = Icons.dashboard;
List<IconData> get itemsList => _items.keys;
Map<IconData, String> _items = {
Icons.home: 'Home',
Icons.drive_eta: 'Delivery',
Icons.shopping_basket: 'Products',
Icons.mail: 'Messages'
};
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
onTap: (int index) {
// todo something
setState(() {
selectedItem = itemsList[index];
});
},
currentIndex: itemsList.indexOf(selectedItem),
items: _items.entries.map((MapEntry<IconData, String> entry) {
return BottomNavigationBarItem(
icon: Icon(entry.key, color: Colors.white),
backgroundColor: entry.key == selectedItem ? Colors.black : Colors.blueGrey,
title: Text(entry.value),
);
}).toList());
}
}