I'm trying to make calendar appointments in flutter using the SfCalendar Widget! I've first of all created the calendar and its events editing page, which takes in different inputs such as startTime and endTime etc.. Once I try to display the events on the calendar they all get displayed at the same date (I have used Provider for state management). Can anyone help me out with this? thanks in advance!
Here is my code:
calendar_garde_screen.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
import '../../database/event_provider.dart';
import '../../model/event.dart';
import 'edit_garde_calendar.dart';
class CalendarGardeScreen extends StatefulWidget {
const CalendarGardeScreen({Key? key}) : super(key: key);
#override
State<CalendarGardeScreen> createState() => _CalendarGardeScreenState();
}
class _CalendarGardeScreenState extends State<CalendarGardeScreen> {
#override
Widget build(BuildContext context) {
final events = Provider.of<EventProvider>(context).events;
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: (() => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const EditGardeCalendarScreen()))),
child: const Icon(Icons.add),
),
appBar: AppBar(
title: const Text(
'Calendrier de garde',
style: TextStyle(color: Colors.black),
),
centerTitle: true,
),
body: SfCalendar(
dataSource: EventDataSource(events),
view: CalendarView.month,
initialDisplayDate: DateTime.now()),
);
}
}
class EventDataSource extends CalendarDataSource {
EventDataSource(List<Event> appointments) {
this.appointments = appointments;
}
Event getEvent(int index) => appointments![index] as Event;
#override
DateTime GetStartTime(int index) => getEvent(index).from;
#override
DateTime GetEndTime(int index) => getEvent(index).to;
#override
String GetSubject(int index) => getEvent(index).title;
#override
bool isAllDay(int index) => getEvent(index).isAllDay;
}
the saveForm() method at the editing page:
Future saveForm() async {
final isValid = _formkey.currentState!.validate();
if (isValid) {
final event = Event(
title: titleController.text,
from: fromDate,
to: toDate,
isAllDay: false,
);
final provider = Provider.of<EventProvider>(context, listen: false);
provider.addEvent(event);
Navigator.of(context).pop();
}
}
my event model:
class Event {
final String title;
final DateTime from;
final DateTime to;
final bool isAllDay;
const Event(
{required this.title,
required this.from,
required this.to,
this.isAllDay = false});
}
Screenshots from the app and the console where i'm printing the startTime and the title of an event:
Thanks in advance!
Update: fixed it by redefining the overridden methods using the official documentation following this link.
Related
In the application, when you click on the "Add Group" button, a page with a text field opens, the user enters the name of the group and it is added to the database and drawn on the screen.
The problem is that only the name of the first group is displayed on the screen, and when you enter the name of the group a second time, it does not appear on the screen. But when you click on Hot Reload, all added groups are displayed. How to make the screen refresh every time a group is added and they are displayed as a list?
In my code, I access the bloc and pass the entered group name to the event:
void _saveGroup() {
final isValid = _formKey.currentState!.validate();
if (isValid) {
BlocProvider.of<NewGroupBloc>(context)
.add(AddGroupEvent(_groupController.text.trim()));
Navigator.pop(context);
}
}
Further, this name is converted into a database module and saved. I then add the model from the database to the list and output it:
lass GroupRepository extends BaseRepository<GroupDao, GroupDBData, Group> {
#override
GroupDao dao = injector<AppDb>().groupDao;
#override
BaseConverter<GroupDBData, Group> converter = GroupDbConverter();
final List<Group> groups = [];
Future<Group> convertGroup(Group group) async {
final convert = converter.outToIn(group);
final groupId = await dao.insert(convert);
Group groupWithID = Group(
id: groupId,
groupName: group.groupName,
);
return groupWithID;
}
Future<List<Group>> getList(Group group) async {
final groupWithID = await convertGroup(group);
groups.add(groupWithID);
return groups;
}
}
class GroupsPage extends StatefulWidget {
const GroupsPage({Key? key}) : super(key: key);
#override
State<GroupsPage> createState() => _GroupsPageState();
}
class _GroupsPageState extends State<GroupsPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Groups'),
),
body: BlocBuilder<NewGroupBloc, NewGroupState>(
builder: (context, state) {
if (state is AddGroupState) {
return ListView.builder(
itemCount: state.groups.length,
itemBuilder: (context, index) {
final group = state.groups[index];
return ListTile(
title: Text(group.groupName),
);
},
);
}
return Container();
},
),
floatingActionButton: Padding(
child: FloatingActionButton.extended(
onPressed: () => _onAddGroup(context),
icon: const Icon(Icons.add),
label: const Text('Add Group'),
),
),
);
}
My bloc:
//bloc
class NewGroupBloc extends Bloc<NewGroupEvent, NewGroupState> {
NewGroupBloc() : super(const NewGroupInitial()) {
on<AddGroupEvent>(
(event, emit) async => emit(
await _addGroup(event),
),
);
}
final _provider = ProviderInjector.instance.addGroup;
Future<NewGroupState> _addGroup(AddGroupEvent event) async => _provider
.addGroup(
Group(groupName: event.groupName),
)
.then(AddGroupState.new);
}
//state
#immutable
abstract class NewGroupState extends Equatable {
const NewGroupState();
#override
List<Object?> get props => [];
}
class NewGroupInitial extends NewGroupState {
const NewGroupInitial();
}
class AddGroupState extends NewGroupState {
const AddGroupState(this.groups);
final List<Group> groups;
#override
List<Object?> get props => [groups];
}
//event
#immutable
abstract class NewGroupEvent extends Equatable {
#override
List<Object?> get props => [];
}
class AddGroupEvent extends NewGroupEvent {
AddGroupEvent(this.groupName);
final String groupName;
#override
List<Object?> get props => [groupName];
}
_onAddGroup:
void _onAddGroup(BuildContext context) {
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute(
builder: (context) => const AddGroupPage(),
),
);
}
class AddGroupState extends NewGroupState {
const AddGroupState(this.groups);
final List<Group> groups;
#override
List<Object?> get props => [this.groups];
}
Try this
I am new in using bloc library with freezed package. I have a scenario where a list of objects is coming from API is displayed. Now the list tile has a Marked as Fav button and clicking upon it a event is trigged and fav bool is toggled and state is emitted.
The Issue :
The value of the object is changed but the UI is not updated accordingly.
Main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_bloc_update/bloc/fakeapi_bloc.dart';
void main() {
runApp(
BlocProvider(
create: (context) => FakeapiBloc(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Scaffold(
body: SafeArea(child: MyHomePage()),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
context.read<FakeapiBloc>().add(const FakeapiEvent.loadData());
super.initState();
}
#override
Widget build(BuildContext context) {
return BlocBuilder<FakeapiBloc, FakeapiState>(
builder: (context, state) {
return state.map(
inital: (_) => const Center(
child: CircularProgressIndicator(),
),
loading: (_) => const Center(
child: CircularProgressIndicator(),
),
loaded: (state) {
return ListView.builder(itemBuilder: (ctx, pos) {
return ListTile(
title: Text(state.posts[pos].title),
trailing: IconButton(
onPressed: () {
context
.read<FakeapiBloc>()
.add(FakeapiEvent.markedFav(pos: pos));
},
icon: Icon(state.posts[pos].isFav
? Icons.favorite
: Icons.favorite_border_outlined),
),
);
});
},
error: (_) => const Center(
child: CircularProgressIndicator(),
),
);
},
);
}
}
Post.dart
import 'dart:convert';
List<Post> postFromJson(String str) =>
List<Post>.from(json.decode(str).map((x) => Post.fromJson(x)));
String postToJson(List<Post> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Post {
Post({
required this.userId,
required this.id,
required this.title,
required this.body,
});
final int userId;
final int id;
final String title;
final String body;
bool isFav = false;
factory Post.fromJson(Map<String, dynamic> json) => Post(
userId: json["userId"],
id: json["id"],
title: json["title"],
body: json["body"],
);
Map<String, dynamic> toJson() => {
"userId": userId,
"id": id,
"title": title,
"body": body,
};
}
bloc.dart
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:freezed_bloc_update/post.dart';
import 'package:http/http.dart' as http;
part 'fakeapi_event.dart';
part 'fakeapi_state.dart';
part 'fakeapi_bloc.freezed.dart';
class FakeapiBloc extends Bloc<FakeapiEvent, FakeapiState> {
FakeapiBloc() : super(const FakeapiState.inital()) {
on<LoadData>(
(event, emit) async {
try {
emit(const FakeapiState.loading());
Uri uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final response = await http.get(uri);
if (response.statusCode == 200) {
emit(FakeapiState.loaded(posts: postFromJson(response.body)));
} else {
emit(const FakeapiState.error(errorMessage: 'Api Call Failed'));
}
} catch (err) {
emit(const FakeapiState.error(errorMessage: 'Api Call Failed'));
}
},
);
on<MarkedFav>((event, emit) {
final previousState = state as Loaded;
previousState.posts[event.pos].isFav =
!previousState.posts[event.pos].isFav;
emit(FakeapiState.loaded(posts: [...previousState.posts]));
});
}
}
events.dart
part of 'fakeapi_bloc.dart';
#freezed
class FakeapiEvent with _$FakeapiEvent {
const factory FakeapiEvent.loadData() = LoadData;
const factory FakeapiEvent.markedFav({required int pos}) = MarkedFav;
}
states.dart
part of 'fakeapi_bloc.dart';
#freezed
class FakeapiState with _$FakeapiState {
const factory FakeapiState.inital() = Initial;
const factory FakeapiState.loading() = Loading;
const factory FakeapiState.loaded({required List<Post> posts}) = Loaded;
const factory FakeapiState.error({required String errorMessage}) = Error;
}
One solution which I did was keeping a bool variable(outside of model class) in state itself and clicking upon the fav toggling the bool value. Which is retriggering the UI update.
your Post class should be converted to a freezed class
I'm new to flutter and im trying to design an application that pulls information via a car numberplate. I have followed this tutorial regarding the API: https://docs.flutter.dev/cookbook/networking/fetch-data. For the API i need to specify the numberplate which i have used this tutorial where its saved text from the textfield: https://docs.flutter.dev/cookbook/forms/retrieve-input. I can't seem to figure out how to get text from the textfield and then make the API call.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
void main() => runApp(const MyApp());
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('https://v1.motorapi.dk/vehicles/'),
headers: {"X-AUTH-TOKEN": "rfrzsucnc7eo3m5hcmq6ljdzda1lz793"});
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
//headers: {"X-AUTH-TOKEN": "rfrzsucnc7eo3m5hcmq6ljdzda1lz793"}
class Album {
final String registration_number;
final String status;
final String type;
final String use;
final String first_registration;
final String vin;
final int doors;
final String make;
final String model;
final String variant;
final String model_type;
final String color;
final String chasis_type;
final String engine_power;
final String fuel_type;
final String RegistreringssynToldsyn;
final String date;
final String result;
Album({
required this.registration_number,
required this.status,
required this.type,
required this.use,
required this.first_registration,
required this.vin,
required this.doors,
required this.make,
required this.model,
required this.variant,
required this.model_type,
required this.color,
required this.chasis_type,
required this.engine_power,
required this.fuel_type,
required this.RegistreringssynToldsyn,
required this.date,
required this.result,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
registration_number: json['registration_number'],
status: json['status'],
type: json['type'],
use: json['use'],
first_registration: json['first_registration'],
vin: json['vin'],
doors: json['doors'],
make: json['make'],
model: json['model'],
variant: json['variant'],
model_type: json['model_type'],
color: json['color'],
chasis_type: json['chasis_type'],
engine_power: json['engine_power'],
fuel_type: json['fuel_type'],
RegistreringssynToldsyn: json['RegistreringssynToldsyn'],
date: json['date'],
result: json['result'],
);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Retrieve Text Input',
home: MyCustomForm(),
);
}
}
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
// Define a corresponding State class.
// This class holds the data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Create a text controller and use it to retrieve the current value
// of the TextField.
final myController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Retrieve Text Input'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: myController,
),
),
floatingActionButton: FloatingActionButton(
// When the user presses the button, show an alert dialog containing
// the text that the user has entered into the text field.
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
// Retrieve the text the that user has entered by using the
// TextEditingController.
content: Text(myController.text),
);
},
);
},
tooltip: 'Show me the value!',
child: const Icon(Icons.text_fields),
),
);
}
}
change your code for this
// Define a corresponding State class.
// This class holds the data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Create a text controller and use it to retrieve the current value
// of the TextField.
TextEditingController myController ;
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
myController=TextEditingController();
futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Retrieve Text Input'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: myController,
),
),
floatingActionButton: FloatingActionButton(
// When the user presses the button, show an alert dialog containing
// the text that the user has entered into the text field.
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
// Retrieve the text the that user has entered by using the
// TextEditingController.
content: Text(myController.text),
);
},
);
},
tooltip: 'Show me the value!',
child: const Icon(Icons.text_fields),
),
);
}
You miss this method
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
I have a list of objects, but I want to change the state of one object to "isLoading" where it will have a different title, etc.
I'm building my list view:
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
body: Obx(() => buildListView(context)));
}
Widget buildListView(BuildContext context) {
return ListView.builder(
itemCount: controller.saveGames.length,
itemBuilder: (context, index) {
final saveGame = controller.saveGames.elementAt(index);
return saveGame.isLoading
? buildListTileIsLoading(context, saveGame)
: buildListTile(context, saveGame);
});
}
ListTile buildListTile(BuildContext context, SaveGame saveGame) {
return ListTile(
onTap: () => controller.process(saveGame)
);
}
The controller:
class SaveGameController extends GetxController {
final RxList<SaveGame> saveGames = <SaveGame>[].obs;
void process(SaveGame saveGame) {
saveGame.working = true;
update();
}
}
Where have I gone wrong here?
edits: Added more code
So despite the fact, I'm only updating one object in the list and not modifying the content of the list (adding/removing objects) I still need to call saveGames.refresh();
An oversight on my end didn't think you'd need to refresh the entire list if you're just changing the property on one of the objects.
Good to know :)
update() is used with GetBuilder()
obs() is used with obx()
you need to make a change on list to update widgets
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/get_navigation/get_navigation.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
onInit: () {
Get.lazyPut(() => SaveGameController());
},
home: const HomePage(),
);
}
}
class HomePage extends GetView<SaveGameController> {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(), body: Obx(() => buildListView(context)));
}
Widget buildListView(BuildContext context) {
return ListView.builder(
itemCount: controller.saveGames.length,
itemBuilder: (context, index) {
final saveGame = controller.saveGames.elementAt(index);
return buildListTile(context, saveGame);
});
}
ListTile buildListTile(BuildContext context, SaveGame saveGame) {
return ListTile(
tileColor: saveGame.working ? Colors.red : Colors.yellow,
title: Text(saveGame.name),
onTap: () => controller.process(saveGame));
}
}
class SaveGameController extends GetxController {
final RxList<SaveGame> saveGames = <SaveGame>[
SaveGame(id: 0, name: 'a', working: false),
SaveGame(id: 1, name: 'b', working: false),
SaveGame(id: 2, name: 'c', working: false)
].obs;
void process(SaveGame saveGame) {
final index = saveGames.indexWhere((element) => element.id == saveGame.id);
saveGames
.replaceRange(index, index + 1, [saveGame.copyWith(working: true)]);
}
}
class SaveGame {
final int id;
final String name;
final bool working;
SaveGame({required this.id, required this.name, required this.working});
SaveGame copyWith({int? id, String? name, bool? working}) {
return SaveGame(
id: id ?? this.id,
name: name ?? this.name,
working: working ?? this.working);
}
}
A simple, but very complicated question: What’s the best way to add a tap to focus functionality for the Flutter camera?
I’ve searched the entire World Wide Web about elegant solutions, but I found nothing.
Do you have an idea?
I might be late but you can try adv_camera package.
Here is a simple example:
import 'package:adv_camera/adv_camera.dart';
import 'package:flutter/material.dart';
class CameraApp extends StatefulWidget {
final String id;
const CameraApp({Key? key, required this.id}) : super(key: key);
#override
_CameraAppState createState() => _CameraAppState();
}
class _CameraAppState extends State<CameraApp> {
List<String> pictureSizes = <String>[];
String? imagePath;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AdvCamera Example'),
),
body: SafeArea(
child: AdvCamera(
initialCameraType: CameraType.rear,
onCameraCreated: _onCameraCreated,
onImageCaptured: (String path) {
if (this.mounted)
setState(() {
imagePath = path;
});
},
cameraPreviewRatio: CameraPreviewRatio.r16_9,
focusRectColor: Colors.purple,
focusRectSize: 200,
),
),
floatingActionButton: FloatingActionButton(
heroTag: "capture",
child: Icon(Icons.camera),
onPressed: () {
cameraController!.captureImage();
},
),
);
}
AdvCameraController? cameraController;
_onCameraCreated(AdvCameraController controller) {
this.cameraController = controller;
this.cameraController!.getPictureSizes().then((pictureSizes) {
setState(() {
this.pictureSizes = pictureSizes ?? <String>[];
});
});
}
}