Sort List if Dropdown entry is changed - android

I am trying to setup a new sort dropdown button which changes the sort order of the entries that are being fetched from the database.
I can sort the list on page load but I cannot change the sort order using the dropdown options.
The code complies without errors.
Any advice what I need to change to make this code work?
class NewList extends StatefulWidget {
#override
State<StatefulWidget> createState() => new _State();
}
class _State extends State<NewList> {
static dates;
List<Entry> _entryList = [];
List<Entry> _sort = [];
StreamController<int> _postsController;
List<Option> options = [];
Option _selectedOption;
void _select(Option option) {
setState(() {
_selectedOption = option;
});
if (_selectedOption.title =='Oldest first') {
_sort.sort((a, b) {
return b.id.compareTo(a.id);
});
} else if (_selectedOption.title =='Newest first') {
_sort.sort((a, b) {
return a.id.compareTo(b.id);
});
}
}
#override
void initState() {
_postsController = new StreamController();
fetchReport();
super.initState();
}
fetchReport() {
fecthEntries(dates.id.toString(), dates.from, dates.to)
.then((value) => {
_entryList.addAll(value),
_postsController.add(1),
setState(() {})
});
}
#override
Widget build(BuildContext context) {
dates = ModalRoute.of(context).settings.arguments;
options = <Option>[
Option(
title: 'Oldest first',
Option(
title: 'Newest first',
];
_selectedOption = options[0];
return Scaffold(
appBar: AppBar(
title: Text('Title'),
actions: <Widget>[
PopupMenuButton<Option>(
onSelected: _select,
icon: Icon(Icons.sort),
itemBuilder: (BuildContext context) {
return options.map((Option option) {
return PopupMenuItem<Option>(
value: option,
child: Text(option.title),
);
}).toList();
},
),
],
),
body: StreamBuilder<int>(
stream: _postsController.stream,
builder: (BuildContext context, AsyncSnapshot<int> result) {
if (result.hasData) {
return showAll();
}
}),
);
}
Widget showAll() {
//This is where I can sort the list onload and setup the default sort
_entryList.sort((a, b) {
return b.id.compareTo(a.id);
});
return ListView.builder(
itemCount: _entryList.length,
itemBuilder: (context, index) {
final entry = _entryList[index];
return reportRow(entry, context);
},
);
}
}
class Option {
const Option({this.title});
final String title;
}

I would try to wrap the sort logic with a setState() as well. Have you tried?

Related

ProviderNotFoundException - Could not find the correct Provider

Hello fellow Flutter Devs
I am pretty new to Flutter and I am building a bottom navigation bar with three pages and on one page the will be a list with items from an API using the bloc architecture. However I am getting the error in the title located to the ListPage().
ProviderNotFoundException (Error: Could not find the correct Provider above this ListPage Widget
ListPage():
class ListPage extends StatelessWidget {
const ListPage({super.key});
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => ListCubit(
repository: context.read<ListRepository>(),
)..fetchList(),
child: const ItemListView(),
);
}
}
class ItemListView extends StatelessWidget {
const ItemListView({super.key});
#override
Widget build(BuildContext context) {
final state = context.watch<ListCubit>().state;
switch (state.status) {
case ListStatus.failure:
return const Center(child: Text('Oops something went wrong!'));
case ListStatus.success:
return ItemView(items: state.items);
case ListStatus.loading:
return const Center(child: CircularProgressIndicator());
}
}
}
class ItemView extends StatelessWidget {
const ItemView({super.key, required this.items});
final List<Item> items;
#override
Widget build(BuildContext context) {
return items.isEmpty
? const Center(child: Text('no content'))
: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ItemTile(
item: items[index],
);
},
itemCount: items.length,
);
}
}
class ItemTile extends StatelessWidget {
const ItemTile({
super.key,
required this.item,
});
final Item item;
#override
Widget build(BuildContext context) {
return Material(
child: ListTile(
leading: Text('#${item.id}'),
title: Text(item.position),
),
);
}
}
The ListPage() is in the HomePage():
class HomePage extends StatelessWidget {
const HomePage({super.key});
static Route<void> route() {
return MaterialPageRoute<void>(builder: (_) => const HomePage());
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<BottomNavigationBloc, BottomNavigationState>(
builder: (BuildContext context, BottomNavigationState state) {
if (state is PageLoading) {
return Center(child: CircularProgressIndicator());
}
if (state is ListPageLoaded) {
return ListPage();
}
if (state is SettingsPageLoaded) {
return SettingsPage(
biometrics: state.biomentrics,
version: state.version,
env: state.env,
);
}
return Container();
},
),
bottomNavigationBar:
BlocBuilder<BottomNavigationBloc, BottomNavigationState>(
builder: (BuildContext context, BottomNavigationState state) {
return BottomNavigationBar(
...
And last but not least, my app.dart:
class App extends StatelessWidget {
const App({
super.key,
required this.authenticationRepository,
required this.userRepository,
required this.listRepository,
required this.settingsRepository,
});
final AuthenticationRepository authenticationRepository;
final UserRepository userRepository;
final ListRepository listRepository;
final SettingsRepository settingsRepository;
#override
Widget build(BuildContext context) {
return RepositoryProvider.value(
value: authenticationRepository,
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (_) => AuthenticationBloc(
authenticationRepository: authenticationRepository,
userRepository: userRepository,
),
),
BlocProvider(
create: (_) => BottomNavigationBloc(
listPageRepository: listRepository,
settingsPageRepository: settingsRepository))
],
child: const AppView(),
));
}
}
class AppView extends StatefulWidget {
const AppView({super.key});
#override
State<AppView> createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
final _navigatorKey = GlobalKey<NavigatorState>();
NavigatorState get _navigator => _navigatorKey.currentState!;
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
builder: (context, child) {
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
switch (state.status) {
case AuthenticationStatus.authenticated:
_navigator.pushAndRemoveUntil<void>(
HomePage.route(),
(route) => false,
);
break;
case AuthenticationStatus.unauthenticated:
_navigator.pushAndRemoveUntil<void>(
LoginPage.route(),
(route) => false,
);
break;
case AuthenticationStatus.unknown:
break;
}
},
child: child,
);
},
onGenerateRoute: (_) => SplashPage.route(),
);
}
}
As the error says it could not find the right provider I probably need to add it somewhere in my project but i don't know where because I added the ListRepository in my App(). Do I need to add in the _AppViewState() as well?
Thanks for your help and I will give additional information in the comments if you need so.

Flutter Switch doesn't work in Modalbottomsheet

My setup:
class Start_Page extends StatefulWidget {
#override
StartPageState createState() => StartPageState();
}
class StartPageState extends State<Start_Page> {
#override
Widget build(BuildContext context){
return Scaffold(
body: Container(
child: ElevatedButton(
style: ButtonStyle(),
onPressed: () {
createUserModalBottomSheet(context);
},
child: Text("Start"),
)
)
);
}
}
void createUserModalBottomSheet(context){
showModalBottomSheet(context: context, builder: (BuildContext bc) {
return Container(
child: Switch(value: true, onChanged: (value) => {value = !value}, activeColor:
Colors.grey)
);
}
}
The Problem is that the switch won't change his value. The Modalbottomsheet appears but won't update changes/states.
Does anyone know a solution?
Use StatefulBuilder to update UI inside showModalBottomSheet. Second issue is you need to use a bool variable to hold value.
class StartPageState extends State<Start_Page> {
bool switchValue = false;
///......
void createUserModalBottomSheet(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return StatefulBuilder(
builder: (context, setStateSB) => Container(
child: Switch(
value: switchValue,
onChanged: (value) {
setState(() {
// update parent UI
switchValue = value;
});
setStateSB(() {
// update inner dialog
switchValue = value;
});
},
activeColor: Colors.grey),
),
);
});
}
#override
Widget build(BuildContext context) {
.........

type 'Future<dynamic>' is not a subtype of type 'List<Application>?'

I am trying to create a search widget in flutter, for list I used the application list using device_apps (https://pub.dev/packages/device_apps). But Im getting an error "type 'Future' is not a subtype of type 'List?'" . To create the search widget I used help from this link.
Here is my code:
import 'dart:async';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart';
class SearchWidget extends StatelessWidget {
SearchWidget({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
TextField(onChanged: _filter),
StreamBuilder<List<Application>>(
initialData: lelist(),
stream: _stream,
builder:
(BuildContext context, AsyncSnapshot<List<Application>> snapshot) {
print(snapshot.data);
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(snapshot.data[index].appName);
},
);
},
)
],
),
);
}
}
StreamController<List<Application>> _streamController = StreamController<List<Application>>();
Stream<List<Application>> get _stream => _streamController.stream;
_filter(String searchQuery) {
List<Application> _filteredList = lelist()
.where((Application app) => app.appName.toLowerCase().contains(searchQuery.toLowerCase()))
.toList();
_streamController.sink.add(_filteredList);
}
lelist() async {
List<Application> _dataFromQuerySnapShot = await DeviceApps.getInstalledApplications(includeAppIcons: true);
_dataFromQuerySnapShot.sort((a, b) {
return a.appName.toString().toLowerCase().compareTo(b.appName.toString().toLowerCase());
});
return _dataFromQuerySnapShot;
}
I think there it is unnecessary to load and sort the installed apps on every keystroke and therefore put all of this logic into initState. This also makes your _filter function synchronous.
import 'dart:async';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart';
class SearchWidget extends StatefulWidget {
SearchWidget({Key key}) : super(key: key);
#override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final _streamController = StreamController<List<Application>>();
List<Application> _dataFromQuerySnapShot;
Stream<List<Application>> get _stream => _streamController.stream;
#override
void initState() {
DeviceApps.getInstalledApplications(includeAppIcons: true)
.then((List<Application> apps) {
_dataFromQuerySnapShot = apps;
_dataFromQuerySnapShot.sort((a, b) {
return a.appName
.toString()
.toLowerCase()
.compareTo(b.appName.toString().toLowerCase());
});
_streamController.sink.add(_dataFromQuerySnapShot);
});
super.initState();
}
#override
void dispose() {
_streamController.close();
super.dispose();
}
_filter(String searchQuery) {
final filteredApplications = _dataFromQuerySnapShot
.where((Application app) =>
app.appName.toLowerCase().contains(searchQuery.toLowerCase()))
.toList();
_streamController.sink.add(filteredApplications);
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
TextField(onChanged: _filter),
StreamBuilder<List<Application>>(
stream: _stream,
builder: (BuildContext context,
AsyncSnapshot<List<Application>> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return snapshot.data.isEmpty
? Center(child: Text('Empty'))
: ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Text(snapshot.data[index].appName);
},
);
},
)
],
),
);
}
}
I think you need to add asList after calling your future.
lelist() async {
List<Application> _dataFromQuerySnapShot = await
DeviceApps.getInstalledApplications(includeAppIcons: true);
_dataFromQuerySnapShot.sort((a, b) {
return
a.appName.toString().toLowerCase().compareTo(b.appName.toString().toLowerCase());
}) as List;
return _dataFromQuerySnapShot;
}

The method was called on null error in flutter

I need your help, below is my simple code trying to get data from API, I'm using flutter and postman for testing I don't know what exactly is wrong but i'm getting the error (The method [] was called on null.) Anyone who can help!!
class FetchTarget extends StatefulWidget {
#override
// _FetchTargetState createState() => _FetchTargetState();
// return fetchTarget();
// };
State<StatefulWidget> createState() {
return _FetchTargetState();
}
}
class _FetchTargetState extends State<FetchTarget> {
fetchTarget() async {
var res = await http.get("http://192.168.1.00:8000/api/taarifa");
if (res.statusCode == 200) {
var obj = json.decode(res.body);
return obj;
}
}
#override
void initState() {
fetchTarget();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder(
future: fetchTarget(),
builder: (context, snapshot) {
if (snapshot != null) {
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index]['name']),
subtitle: Text(snapshot.data[index]['date']),
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
));
}
}
in the end i expect it to return lists of taarifa and display in in listview in my mobile
Rember that for a correct implementation of FutureBuilder you must pay attention that the async method must not called more than once.
Moreover I noticed that you don't return anything if there is an error.
That could be the problem:
This is a simple solution:
import 'dart:convert';
import 'package:flutter/material.dart';
class FetchTarget extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _FetchTargetState();
}
}
class _FetchTargetState extends State<FetchTarget> {
Future _fetched;
#override
void initState() {
_fetched = _fetchTarget();
super.initState();
}
Future<dynamic> _fetchTarget() async {
var res = await http.get("http://192.168.1.00:8000/api/taarifa");
if (res.statusCode == 200) {
var obj = json.decode(res.body);
return obj;
}else {
return <String, dynamic>{} ; //an empty map as error, but you must check better if the http get returned an exception
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder(
future: _fetched,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemBuilder: (context, index) {
itemCount : snapshot.data.length,
return ListTile(
title: Text(snapshot.data[index]['name']),
subtitle: Text(snapshot.data[index]['date']),
);
},
);
} else {
return CircularProgressIndicator();
}
},
),
));
}
}
Another better solution is to use Flutter Hooks.
Here after a little example:
class MyWidget extends HookWidget {
#override
Widget build(BuildContext context) {
final future = useMemoized(() {
_fetchTarget();
});
return FutureBuilder<String>(
future: future,
builder: (context, snapshot) {
return ....;
}
);
}
}

Unable to SetState() in Flutter

AppScreenC will give all the installed apps in a ListView
class AppScreenC extends StatefulWidget {
#override
_AppScreenCState createState() => _AppScreenCState();
}
List<Application> apps;
getApps() async {
if (apps == null) {
apps = await DeviceApps.getInstalledApplications(
onlyAppsWithLaunchIntent: true,
includeSystemApps: true,
includeAppIcons: true);
apps.sort((a, b) => a.appName.compareTo(b.appName));
}
}
ListView with all the installed apps are getting displayed in screen. I'm trying to change the icons based on onTap event.
But clicking on a list, icons are not changing.
class _AppScreenCState extends State<AppScreenC> {
final _app = <Application>[];
#override
Widget build(BuildContext context) {
return _buildApps();
}
Widget _buildApps() {
getApps();
return ListView.builder(itemBuilder: (BuildContext context, int index) {
_app.addAll(apps);
return _buildRow(_app[index]);
});
}
Widget _buildRow(ApplicationWithIcon app) {
bool selected = false;
return ListTile(
leading: Image.memory(app.icon, height: 40),
trailing:
Icon(selected ? Icons.check_circle : Icons.check_circle_outline),
title: Text(app.appName),
onTap: () {
selected = !selected;
// print("$selected");
// print("${app.apkFilePath}");
setState(() {});
},
);
}
}
itemCount: missing in ListView.Builder
Widget _buildApps() {
getApps();
return ListView.builder(itemBuilder: (BuildContext context, int index) {
_app.addAll(apps);
return _buildRow(_app[index]);
}, itemCount: _app.length);
}
Also,
class _AppScreenCState extends State<AppScreenC> {
bool selected = false; // this should be at the top as it will persist the value
class _AppScreenCState extends State<AppScreenC> {
bool selected = false;
Widget _buildRow(ApplicationWithIcon app) {
//bool selected = false; not here

Categories

Resources