I have created a table from an imported CSV file, I have set this table to be hidden as I don't want the user to actually see the table.
I have also created a custom widget to be reused several times throughout my app, it's just a container that displays some text. Here is the code for the table:
import 'package:flutter/material.dart';
import 'package:csv/csv.dart';
import 'package:flutter/services.dart' show rootBundle;
class RouteNameTable extends StatefulWidget {
const RouteNameTable({Key key}) : super(key: key);
#override
_RouteNameTableState createState() => _RouteNameTableState();
}
class _RouteNameTableState extends State<RouteNameTable> {
List<List<dynamic>> data = [];
void loadAsset() async {
final myData = await rootBundle.loadString("assets/routes.csv");
List<List<dynamic>> csvTable = const CsvToListConverter().convert(myData);
data = csvTable;
setState(() {});
}
#override
void initState() {
super.initState();
loadAsset();
}
#override
Widget build(BuildContext context) {
return Visibility(
visible: false,
child: SingleChildScrollView(
child: Table(
border: TableBorder.all(width: 1.0),
children: data.map((item) {
return TableRow(
children: item.map((row) {
return Container(
color: Colors.blueGrey,
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Text(
row.toString(),
style: const TextStyle(fontSize: 20.0),
),
),
);
}).toList());
}).toList(),
),
),
);
}
}
The custom widget I created is just a container that displays a number, name and has a colour. When inserting the widget I use the following code
const RouteTemplate(
routeNumber: '33',
routeName: 'Corstorphine - Caversham - City - Wakari',
routeColor: Color(0xFFF067A6),
),
This works correctly. However, what I want to do is replace the text (such as '33') that I have typed with data from the table I created. It will still say 33 but instead of me typing it, it retrieves the first column in the second row as routeNumber, the second column in the second row as routeName and so on. I would like this as the CSV file may change, and instead of having to add/remove/change the route number, once the new CSV file is added it will do it automatically.
Thanks
Related
I want to show the last 5 list in list view, I am able to do this for 1st 5 list but not for last 5. How can I get the last 5 list in my emulator.
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:restapipro/models/posts.dart';
import 'package:restapipro/services/remote_service.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Post>? posts;
var isLoaded = false;
var reverse=false;
#override
void initState() {
super.initState();
//fetch data from API
getData();
}
Future getData() async {
posts = await RemoteService().getPosts();
if (posts != null) {
setState(() {
isLoaded = true;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: const Text('Posts'),),
body: Visibility(visible: isLoaded,
replacement: const Center(child: CircularProgressIndicator(),),
child: ListView.builder(reverse: true,itemCount: 5,
itemBuilder: (context, index) {
return Container(padding: const EdgeInsets.all(16),
child: Row(children: [Container(height: 50,width: 50,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12),
color: Colors.grey[300],),),
const SizedBox(width: 16),
Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start,
children: [Text(posts![index].title, maxLines: 2,overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 24,fontWeight: FontWeight.bold,),),
Text(posts![index].body ?? '',
maxLines: 3,
overflow: TextOverflow.ellipsis,)
,],),),]),);
}),),);
}
}
I just want to know how can I get the widget to print last 5 list from n number of list.
I'm only answering what I understood from your question as the block of code seems messy. I suppose you only want to render last 5 items of your List that you got from API and I also suppose you only need to render those last 5 items ignoring the other.
Taking above condition in mind, let's keep only the last 5 items of the fetched lists into your post variable.
For this, change your getPosts() function to this:
Future getPosts() async{
List<Post>? fetchedPosts = await RemoteService().getPosts();
if(fetchedPosts != null){
posts = fetchedPosts.where((e) => (fetchedPosts.length - fetchedPosts.indexOf(e)) <= 5 ).toList();
setState((){
isLoaded = true;
});
}
}
This'll set last 5 items of the list in post if the length of list is greater than 5, and will add all the items in the post list if the length is lesser.
Implement this in your function and you're good to go.
I already have an app which has a search bar and can search and find the search results in the background. However, I don't know how to display it right below my search bar, or anywhere in the HomeScreen.
Please help me connect the two parts.
The current HomePage looks something like this,
import 'package:flutter/material.dart';
import 'dart:developer' as devtools show log;
...
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Initial marker location
LatLng markerPoint = LatLng(12.9716, 77.5946);
final LocationService _locationService = getIt<LocationService>();
...
LatLng get currentMarkerPoint => markerPoint;
#override
Widget build(BuildContext context) {
return Stack(
children: [
FlutterMap(
...
SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Here I have the card which contains the search bar
// I want the search results to be displayed right below this.
Card(
child: TextField(
decoration: const InputDecoration(
prefixIcon: Icon(Icons.location_on_outlined),
hintText: "Search for a location",
contentPadding: EdgeInsets.all(16.0),
),
onChanged: (text) async {
if (text.isEmpty || text.length < 3) {
return;
}
LatLng pos = await _locationService.currentLocation;
var locations = await _locationSearch
.searchNearCurrentPosition(text, pos);
},
),
),
],
),
),
),
],
);
}
}
I have managed to work around with some backend and managed to store my search results inside of a widget in a class named HomePageSearchListView which is defined as below
import 'package:flutter/material.dart';
import 'package:geoalarm/services/location_search/location_entities.dart';
import 'dart:developer' as devtools show log;
typedef LocationCallback = void Function(LocationEntity location);
class HomePageNotesListView extends StatelessWidget {
final List<LocationEntity> locations;
final LocationCallback onTap;
const HomePageNotesListView({
Key? key,
required this.locations,
required this.onTap,
}) : super(key: key);
#override
Widget build(BuildContext context) {
devtools.log(locations.length.toString());
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: locations.length,
itemBuilder: (context, index) {
final location = locations.elementAt(index);
devtools.log(location.country);
return ListTile(
title: Text(location.name),
subtitle:
Text("${location.state}, ${location.country}, ${location.point}"),
onTap: () => onTap(location),
);
},
);
}
}
The problem is that I do not know how to use this class that I made appear below the search bar in my homepage.
Help would be appreciated.
after a bit of research and help from another answer to this same question. (Which is now deleted) I have found solution to this problem.
You could just use the Search Delegate class which is an already pre implemented search UI for flutter except that you'd have to implement the results that you want to show and the suggestions you have to show.
A whole lot of customisation is not available but this should work for most apps which are struggling to implement a search screen.
I am working on a real estate app where I would like to display a list of properties, that is retrieved from the Algolia database, and search them from the search input field by typing the I.D of the properties. Like this
I have successfully linked/setup Firebase & Algolia. And I am able to display the properties on the screen, using infinite_scroll_pagination and algolia_helper_flutter packages.
The problem I am facing is I cannot search the houses by typing the I.D of the properties.
Please check out my code, tell me where I went wrong. Thank you.
Best,
class HousesListView extends StatefulWidget {
const HousesListView({Key? key}) : super(key: key);
#override
State<HousesListView> createState() => _HousesListViewState();
}
class _HousesListViewState extends State<HousesListView> {
// textController for search box input
final _searchTextController = TextEditingController();
// pageController from infinite_scroll_pagination package
final PagingController<int, MdlAlgoliaProperties> pagingController =
PagingController(firstPageKey: 0);
/// Component holding search filters from algolia_helper_flutter package
final _filterState = FilterState();
// search houses in Algolia Database
final _houseDatabase = HitsSearcher.create(
applicationID: AlgoliaCredentials.applicationID,
apiKey: AlgoliaCredentials.apiKey,
state: const SearchState(
indexName: AlgoliaCredentials.hitsIndex,
facetFilters: ['a2-propertyType: House']));
// stream and display list of properties on the screen
Stream<PropertiesPage> get displayPropertiesOnThePage =>
_houseDatabase.responses.map(PropertiesPage.fromResponse);
/// Get stream of search result, like the number of the result from the search box
Stream<SearchMetadata> get searchMetadata =>
_houseDatabase.responses.map(SearchMetadata.fromResponse);
#override
void initState() {
super.initState();
// listen to keystroke & query the results by the letters that user types in
_searchTextController
.addListener(() => _houseDatabase.query(_searchTextController.text));
// load properties on the page
displayPropertiesOnThePage.listen((properties) {
if (properties.pageKey == 0) pagingController.refresh();
pagingController.appendPage(
properties.alogliaPPT, properties.nextPageKey);
}).onError((error) => pagingController.error = error);
// error here!
// this loads the list of house successfully and properly when its enabled, but search does not work anymore
// but, when this disable, the search works, but it does not load the list of houses anymore
pagingController.addPageRequestListener((pageKey) =>
_houseDatabase.applyState((state) => state.copyWith(page: pageKey))); //<= error occur in this line
// connect database and filter state
_houseDatabase.connectFilterState(_filterState);
// pageController listens to filterState
_filterState.filters.listen((_) => pagingController.refresh());
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBarTitle(context, 'List of Houses'),
backgroundColor: ZayyanColorTheme.zayyanGrey,
endDrawer: const Drawer(
width: 350,
child: HouseFilter(),
),
body: Center(
child: Column(
children: [
SizedBox(
height: 44,
child: TextField(
controller: _searchTextController,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'Enter a search term',
prefixIcon: Icon(Icons.search),
),
),
),
StreamBuilder<SearchMetadata>(
stream: searchMetadata,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
}
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text('${snapshot.data!.nbHits} hits'),
);
},
),
Expanded(
child: _hits(context),
),
],
),
),
);
}
Widget _hits(BuildContext context) {
return PropertyHitsListView(
pagingController: pagingController,
noItemsFound: (context) => const NoResultsView(),
onHitClick: (objectID) {
print(objectID);
},
);
}
#override
void dispose() {
_searchTextController.dispose();
_houseDatabase.dispose();
_filterState.dispose();
pagingController.dispose();
super.dispose();
}
}
I have a situation where I have a listview of containers, and I would like when I double tap each container another container pops up below with information. Currently what I am trying to do is wrap each container within a column and do something like:
onDoubleTap() {showBox = true}, and in the column have code:
children: [post(), showbox == true? infobox() : container()] but I am not sure of the correct implementation. Any help would be great!
you should maintain a list of containers:
class ContainerAdder extends StatefulWidget {
const ContainerAdder({Key? key}) : super(key: key);
#override
_ContainerAdderState createState() => _ContainerAdderState();
}
class _ContainerAdderState extends State<ContainerAdder> {
List<Widget> containers = <Widget>[];
Random random = Random();
List<Color> colors = [
Colors.blue,
Colors.green,
Colors.red,
Colors.orange,
Colors.purple,
Colors.pink,
Colors.teal,
Colors.yellow,
];
addContainer() {
setState(() {
int r = random.nextInt(colors.length);
containers.add(
InkWell(
onDoubleTap: () => addContainer(),
child: Container(
margin: const EdgeInsets.only(bottom: 1.0),
height: 50.0,
color: colors[r],
),
),
);
});
}
#override
void initState() {
super.initState();
addContainer();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [...containers],
),
);
}
}
As you can notice, the method addContainer() adds a container which is wrapped in an InkWell to have the tap listener. The doubleTap calls the method addContainer().
I simply spread the containers list inside ListView widget.
In the addContainer() method, I wrap the codes inside setState() so as to refresh the tree. You can use any other state management architecture if you so wish.
For the first time, I call addContainer() inside initState(), in order to populate the list with the first element.
I have a TabBarView with two tabs in main widget. First tab includes gridview with cards. Cards use parent widget (MyHomePage) as listener to listen in-card button clicks.
When i click on button in some card, listener impl. must open second Tab and pass selected Excursion to it. But when I do it, at first iteration, ExcursionEditor(currentExcursion) says, that argument is null, but parent build says, that it is not. If I resize my browser, it calls global rebuild and currentExcursion reach last build value.
So, i cant understand, why MyHomePage build doesn't affect on TabBarView content with arguments passed by constructor
class MyHomePage
import 'package:flutter/material.dart';
import 'package:questbuilder/api/content_manager.dart';
import 'package:questbuilder/model/excursion.dart';
import 'package:questbuilder/pages/tab_editor.dart';
import 'package:questbuilder/pages/tab_my_excursions.dart';
import 'package:questbuilder/widgets/excursion_preview_card.dart';
import 'package:logger/logger.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with TickerProviderStateMixin
implements ExcursionCardInteractionListener {
Logger logger = Logger();
Excursion currentExcursion;
TabController tabController;
#override
void initState() {
super.initState();
print("INIT STATE FOR HOME PAGE");
tabController = TabController(vsync: this, length: 2);
}
#override
Widget build(BuildContext context) {
var screenSize = MediaQuery.of(context).size;
print("HOME PAGE BUILD currentExcursion = ${currentExcursion?.toJson()}");
return Scaffold(
extendBodyBehindAppBar: true,
appBar: PreferredSize(
preferredSize: Size(screenSize.width, 1000),
child: Container(
color: Colors.black,
child: Padding(
padding: EdgeInsets.fromLTRB(10, 10, 30, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(children: [
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: Text('QUESTBUILDER',
style: TextStyle(color: Colors.white))),
SizedBox(width: screenSize.width / 20),
Container(
width: screenSize.width / 6,
child: TabBar(
labelPadding: EdgeInsets.fromLTRB(10, 0, 10, 10),
indicatorColor: Colors.white,
controller: tabController,
tabs: [
Tab(text: "Мои экскурсии"),
Tab(text: "Редактор"),
]))
]),
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
child: Row(
children: [
FlatButton.icon(
label: Text("Создать экскурсию"),
icon: Icon(Icons.add),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(40.0)),
textColor: Colors.white,
color: Colors.green,
onPressed: () {
createExcursion();
}),
SizedBox(
width: 40,
),
InkWell(
onTap: () {},
child: Text(
'Вход',
style: TextStyle(color: Colors.white),
),
)
],
)),
],
),
),
),
),
body: Padding(
padding: EdgeInsets.all(15),
child: TabBarView(
controller: tabController,
children: [
// Set listener to cards in this widget to prerform 'edit' clicks
MyExcursionsTab(this),
ExcursionEditor(currentExcursion)
],
)));
}
// Here i call setState from cards
#override
void editExcursion(Excursion excursion) {
setState(() {
currentExcursion = excursion;
});
tabController.animateTo(1);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
void createExcursion() {
ContentManager.client.createExcursion(0).then((value) {
currentExcursion = value;
editExcursion(currentExcursion);
});
}
}
class ExcursionEditor
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:questbuilder/api/content_manager.dart';
import 'package:questbuilder/model/excursion.dart';
import 'package:questbuilder/model/excursion_content.dart';
import 'package:questbuilder/model/excursion_data.dart';
import 'package:questbuilder/model/picture.dart';
class ExcursionEditor extends StatefulWidget {
Excursion excursion;
ExcursionEditor(this.excursion);
#override
State<StatefulWidget> createState() => ExcursionEditorState();
}
class ExcursionEditorState extends State<ExcursionEditor> {
ExcursionData currentData;
ExcursionContent currentContent;
Excursion excursion;
List<Picture> pictures = [];
#override
void initState() {
super.initState();
print("INIT EDITOR widget.excrusion = ${widget.excursion?.toJson()}");
// At this point, after call setState() in HomePage widget.excrusion is always null
// until I resize browser, thereby calling global state reset
//
if (widget.excursion != null)
ContentManager.client.getPictureList(widget.excursion.id).then((value) {
pictures.addAll(value);
print(pictures);
});
}
#override
Widget build(BuildContext context) {
excursion = widget.excursion;
print("BUILD EDITOR excursion = ${widget.excursion?.toJson()}");
return excursion != null
? Container()
: Container(
child: Align(
alignment: Alignment.center,
child: Text("Выберите экскурсию для редактирования")));
}
}
Log of first launch and card click build sequence:
HOME PAGE BUILD currentExcursion = null
HOME PAGE BUILD currentExcursion = {id: 1}
INIT EDITOR widget.excrusion = null
BUILD EDITOR excursion = null
After browser window resize
HOME PAGE BUILD currentExcursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
HOME PAGE BUILD currentExcursion = {id: 1}
BUILD EDITOR excursion = {id: 1}
After screen resize problem still appear, just replacing null value in editor with old Excursion. New clicks on cards doesn't have effect, setState in callback still not update.
I've tried to bind it on static stream listeners, on TabController listener - it just look like TabBarView late for 1 build cycle of arguments update. Maybe there are some similar questions, but i've done all from thouse answers and got nothing
I am not really sure, but it seems like race condition between setState and _tabController.animateTo(1); because they both try to rebuild the child ExcursionEditor(currentExcursion)
If you print the excursion in ExcursionEditor constructor, you will see the updated value. But at the end the value not reach the build function.
The simple workaround is changing editExcursion to the async function and add a small delay between this 2 actions. Otherwise you can try to use other way to pass data between widgets (like provider)
#override
Future editExcursion(Excursion excursion) async {
setState(() {
currentExcursion = excursion;
});
await Future.delayed(Duration(milliseconds:50));
tabController.animateTo(1);
}