My task is to allow the user to build a polygon on the map. I use the flutter_map library and openstreetmaps. I do not know how to complete the building of the polygon.
Now, by the very first onTap, an IconButton appears on the map. I want to make this IconButton active so that when the user onPress on it: (1) the icon changes, (2) returns (and saves for passing to the backend) a set of points that the user tapped during the polygon building process, and (3) the polygon building process stops (adding new points became impossible).
I think, the challenge comes down to how callback setState inside another setState while retaining functionality. But maybe there is another solution.
My code is below. I can provide the whole code if needed.
I would appreciate any ideas.
class _HomePageState extends State<HomePage> {
...
List<Marker> markers = [];
List<LatLng> polygonList = [];
bool singleTap = false;
...
options: MapOptions(
...
onTap: (latlng) {
if (singleTap) {
setState(() {
if (markers.length == 0) {
markers.add(
Marker(
point: latlng,
builder: (ctx) => IconButton(
icon: Icon(Icons.favorite),
onPressed: () {***HERE the process should be stopped with changing the icon, no new points;***
setState(() {});
},
),
),
);
} else {
markers.add(
Marker(...),
),
);
}
polygonList.add(latlng);
});
}
}),
An answer is very simple: nothing special to do with it, just keep in mind that "other layers may intercept the gestures". I just changed the order of layers, and now my Marker is active.
Related
I am trying the editable, drag and expand event on my FullCalendar.
First of all, I am using React.
To describe the problem:
If I have this 2 events on my calendar with same group:
before drag and drop
Event1 is Workshop#1pm, Event 2 is Date#5pm
When I drag and drop Workshop to 12 pm, the same will goes to Date and it will located at 4pm, at the same time.
after drag and drop
Same thing happen with changing duration.
I only want single event to be affected with the gesture, not all event on its group.
So far, I tried to read the documentation thoroughly but I could not find any settings related to it. I also tried to search forums but I never found similar problem.
I am suspecting it has something to do with my complex style of populating calendar with event.
constructor(props) {
super(props);
this.state = {
myEventSource: [],
}
// function the fetch event from google API
loadEvents = async () => {
await fetch(fetchCommand)
.then(res => res.json())
.then((res) => {
// making the loop
res.result.forEach(result => {
// when get result, it will compose the events object to be
// stored in this.state.myEventSource
var tempEvent = [];
result[0].eventsInfo.forEach(event => {
var evt = {
id: event.id,
groupId: result[0].userName,
title: event.summary,
allDay: event.start.date!=null?true:false,
start: event.start.dateTime!=null?event.start.dateTime?.toString():event.start.date?.toString(),
end: event.end.dateTime!=null?event.end.dateTime?.toString():event.end.date?.toString(),
}
tempEvent.push(evt);
});
this.TempEventSource[result[0].userName] = tempEvent;
});
});
...
this.setState({ myEventSource: this.updateCalendarOnTabChange(this.state.tabIndex), firstLoad: true });
}
render() {
return(
<FullCalendar
plugins={[ timeGridPlugin, dayGridPlugin, interactionPlugin]} //
editable={true}
...
eventSources={this.state.myEventSource}
/>
)
}
I am doing a project where I have to keep an favorite icon and a selected fvrt list... Now using sqlflite .. I have done it.. when the user presses the favorite border icon it get changed to red color and the data saves in the favorite list.. when user pressses again in the same button .. the data gets delated from the list and the favorite button change to ist default color... but what i am not able to do is.. the favorite button is default false.. so even if the data is collected in the fvrt list .. all the fvrt button shows _fvrt default favorite btn when i start the app ...
i was wondering how can i check the data in the initState() , if the data already exit in database it fvrt btn will remain red..
here's a little code of the conditon that i haved used .
Widget _buildRow(String pair) {
final bool alreadySaved = _saved.contains(pair);
print("Already saved $alreadySaved");
print(pair);
return IconButton(
icon: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color:alreadySaved? Colors.red : Colors.white,
),onPressed: (){
setState(() {
if (alreadySaved) {
_saved.remove(pair);
_deleteEmployee(pair);
} else {
_saved.add(pair);
_insert(pair);
}
});
},
);
}
Reading data from your database is an async function - it takes some time. What you can do, is to create a loading state, and show a loading indicator, until the async function finishes.
import 'package:flutter/material.dart';
class MyClass extends StatefulWidget {
#override
_MyClassState createState() => _MyClassState();
}
class _MyClassState extends State<MyClass> {
bool isLoading = false;
List _saved = [];
#override
void initState() {
// Note that you cannot use `async await` in initState
isLoading = true;
_readFromDataBase().then((savedStuff) {
_saved = savedStuff;
isLoading = false;
});
super.initState();
}
#override
Widget build(BuildContext context) {
return !isLoading ? _buildRow("myPair") : CircularProgressIndicator();
}
Widget _buildRow(String pair) {
final bool alreadySaved = _saved.contains(pair);
print("Already saved $alreadySaved");
print(pair);
return IconButton(
icon: new Icon(
alreadySaved ? Icons.favorite : Icons.favorite_border,
color:alreadySaved? Colors.red : Colors.white,
),onPressed: (){
setState(() {
if (alreadySaved) {
_saved.remove(pair);
_deleteEmployee(pair);
} else {
_saved.add(pair);
_insert(pair);
}
});
},
);
}
}
Alternatively you can check the FutureBuilder Widget. Here is the official documentation: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
I have a StatefulWidget that creates an AnimatedCrossFade widget in didUpdateWidget(), and saves it as animation. Here's a pared down example:
class BackgroundImage extends StatefulWidget {
final Color colorA;
final Color colorB;
const BackgroundImage({
this.colorA,
this.colorB,
});
}
class _BackgroundImageState extends State<BackgroundImage> {
Widget _animation;
#override
void didUpdateWidget(Widget old) {
super.didUpdateWidget(old);
_buildBackgroundA(colorA).then((backgroundA) {
_buildBackgroundB(colorB).then(backgroundB) {
print(backgroundA); // this is not null
print(backgroundB); // this is not null
_animation = AnimatedCrossFade(
duration: Duration(seconds: 15),
firstChild: backgroundA,
secondChild: backgroundB,
crossFadeState: someVarToSwitchColors,
? CrossFadeState.showFirst
: CrossFadeState.showSecond,
);
}
}
}
#override
Widget build(BuildContext context) {
return _animation != null ? _animation : Container();
}
}
_buildBackgroundA() and _buildBackgroundB() are async functions that reuturn Future<Widget>. This works fine in my app - didUpdateWidget() is called, my AnimatedCrossFade shows up and animates between the two backgrounds.
However, I'm having trouble finding AnimatedCrossFade in my test. I'm able to find my other Stateless widgets, and also able to find the BackgroundImage widget. I have something like:
await tester.pump();
await tester.pumpAndSettle(Duration(minutes: 1));
expect(
find.descendant(
of: find.byType(BackgroundImage),
matching: find.byType(AnimatedCrossFade)),
findsOneWidget);
This fails as it can't find AnimatedCrossFade.
If I change my build() function to:
#override
Widget build(BuildContext context) {
return AnimatedCrossFade(...);
}
I'm able to find my widget. So I suspect it has something to do with my test expect executing before my _buildBackground() functions are done. I've tried altering the duration in my pump and pumpAndSettle to no effect. How do I force the test to wait more? Is there something else I'm missing?
The test log looks like this (with my prints):
Running Flutter tests.
00:00 +0: ...d/work/.../cl00:00 +0: (setUpAll) 00:00 +0: Test background
init state called
_buildBackgroundA called
init state called
_buildBackgroundB called
...
00:00 +0 -1: Test background
Expected: exactly one matching node in the widget tree
Actual: ?:<zero widgets with type "AnimatedCrossFade" that has ancestor(s) with type "BackgroundImage" (ignoring offstage widgets)>
Which: means none were found but one was expected
This was caught by the test expectation on the following line:
...line 179
about to return from calling _buildBackgroundA
image: Image(image: MemoryImage(_Uint8ArrayView#af55b, scale: 1.0), width: 50.0, height: 200.0, fit: cover, alignment: center, this.excludeFromSemantics: false, filterQuality: low)
about to return from calling _buildBackgroundB
image: Image(image: MemoryImage(_Uint8ArrayView#79291, scale: 1.0), width: 50.0, height: 830.0, fit: cover, alignment: center, this.excludeFromSemantics: false, filterQuality: low)
...
The component:
https://ant.design/components/tree-select/
There is no example with the loadData option.
async getChildren(node) {
console.log(node);
let r = $.get("/tree", {id: node.value})
console.log(await r); // request works
return r;
}
With this code I just see the tree loading and nothing happens. Not an error, but the child nodes are not appended to the tree.
If I don't return a Promise, I get a huge error and a blank page.
this is an example of loadData function:
onLoadData = (treeNode) => {
console.log('load data...');
return new Promise((resolve) => {
setTimeout(() => {
const treeData = [...this.state.treeData];
getNewTreeData(treeData, treeNode.props.eventKey, generateTreeNodes(treeNode), 2);
this.setState({ treeData });
resolve();
}, 500);
});
}
You can find it here with more in-deep examples
To make it clearer:
TreeData is an Array of TreeNode
source
antd tree select uses rc-tree because antd is built on top of rc-components you can see the source
For lazy load you need to manipulate the treeNode, what above snippet
does is: everytime loaded new data it will be a treeNode object, and
will call the onLoadData() callback where you provide the code what
to do with that node. (the sample just append to the state's treeData
variable]
I'm actually trying to toggle a class on an element when user clicked. But unfortunately, my code only set class for single element. It looks like the view is not refreshing for subsequent clicks, even the set class is not removing. But my store is properly updating.
Here is my code.
class MyInterests extends Component {
constructor(props) {
super(props);
this.state = {selected: []};
}
toggleChip(id, index, event){
const { interestChanged } = this.props;
let index = this.state.selected.indexOf(id);
if(index === -1){
this.state.selected.push(id);
}else{
this.state.selected.splice(index, 1);
}
interestChanged(this.state.selected);
}
render() {
const {classes, myinterests: { categories, userinterest } } = this.props;
const getClassNames = (id) => {
return classNames(classes.chip, {[classes.selected]: (userinterest.indexOf(id) !== -1)});
}
return (
/*..... Few element to support styles....*/
{categories.map((data, index) => {
return (
<Chip
key={data._id.$oid}
label={data.name}
onClick={this.toggleChip.bind(this, data._id.$oid, index)}
className={getClassNames(data._id.$oid)}
/>
);
})}
);
}
}
Can anyone tell me what is wrong in this or how can I achieve this?.
Since state is immutable, you cannot use .push on it.
By using this.state.selected.push(id) you're mutating the state thus not signaling react on the change making the change vulnerable to future state updates (remember that setState is asynchronous and changes are batched for single action).
Take a look at this for how to solve it.
In your case, a better way to update the state would be something like this:
// create a copy of the current array
var selected = this.state.selected.slice();
// push the new element to the copy
selected.push(id);
// replace the current array with the modified copy
this.setState({ selected: selected });