如何在 Flutter 上使用我的 API 在表日历上显示事件

Posted

技术标签:

【中文标题】如何在 Flutter 上使用我的 API 在表日历上显示事件【英文标题】:How to show Event on Table Calendar using my API on flutter 【发布时间】:2021-08-24 05:02:50 【问题描述】:

我有用于显示事件日历的 UI,我需要显示来自我的 API 的事件。但我不知道该怎么做。我尝试更改 _event 上的列表,但没有响应。我需要在日历上显示它,这样我的公司日历才能显示该事件。

这是我的 UI 日历

import 'package:intl/intl.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:urus_flutter/persentation/custom_color.dart';
import 'package:urus_flutter/persentation/custom_text_style.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:shared_preferences/shared_preferences.dart';

class Calender extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => CalenderState();


class CalenderState extends State<Calender> 
  CalendarController _controller;
  Map<DateTime, List<dynamic>> _events;
  List<dynamic> _selectedEvents;
  DateTime _selectedDate;
  SharedPreferences prefs;


  @override
  void initState()
    super.initState();
    _controller = CalendarController();
    _events = 
      DateTime(2021, 6, 22) : ['Meeting URUS', 'Testing Danai Mobile', 'Weekly Report', 'Weekly Meeting'],
      DateTime(2021, 6, 25) : ['Weekly Testing'],
      DateTime(2021, 6, 4) : ['Weekly Testing'],
      DateTime(2021, 6, 11) : ['Weekly Testing'],
      DateTime(2021, 6, 18) : ['Weekly Testing'],
    ;
  


  Map<String, dynamic> encodeMap(Map<DateTime, dynamic> map) 
    Map<String, dynamic> newMap = ;
    map.forEach((key, value) 
      newMap[key.toString()] = map[key];
    );
    return newMap;
  

  Map<DateTime, dynamic> decodeMap(Map<String, dynamic> map) 
    Map<DateTime, dynamic> newMap = ;
    map.forEach((key, value) 
      newMap[DateTime.parse(key)] = map[key];
    );
    return newMap;
  


  Widget build(BuildContext context) 
    return Scaffold(
      body: Stack(
        children: [
          ListView(
            padding: EdgeInsets.only(left: 16, right: 16, top: 52, bottom: 126),
            children: [
              Text("Kalender Kegiatan",
                style: CustomTextStlye.proxima_bold_18_black,),
              Container(
                child: Padding(
                  padding: const EdgeInsets.only(top: 28.0),
                  child: Text("Kalender Anda",
                          style: CustomTextStlye.proxima_bold_16_black,),
                ),
              ),

              SizedBox(
                height: 20,
              ),

              Container(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Container(
                      decoration: BoxDecoration(
                        color: Colors.white,
                        borderRadius: BorderRadius.circular(5.0),
                        boxShadow: [
                          BoxShadow(
                              offset: Offset(0, -1),
                              color: CustomColor.border_grey,
                              blurRadius: 3.0,
                              spreadRadius: 1.0)
                        ]
                      ),

                      child: TableCalendar(
                        initialCalendarFormat: CalendarFormat.month,
                        calendarStyle: CalendarStyle(
                          todayColor: Color(0x9429AAE1),
                          todayStyle: CustomTextStlye.proxima_bold_12_white,
                          selectedColor: Color(0xFF29AAE1),
                          selectedStyle: CustomTextStlye.proxima_bold_12_white,
                          weekdayStyle: CustomTextStlye.proxima_bold_12_black,
                          weekendStyle: CustomTextStlye.proxima_bold_12_red,
                          unavailableStyle: CustomTextStlye.proxima_bold_12,
                          holidayStyle: CustomTextStlye.proxima_bold_12_red,
                          markersColor: Color(0xFFA2CD3A),
                        ),
                        headerStyle: HeaderStyle(
                          centerHeaderTitle: true,
                          formatButtonVisible: false,
                          titleTextStyle: CustomTextStlye.proxima_bold_14_black,
                        ),
                        availableCalendarFormats: const CalendarFormat.month: '',,
                        startingDayOfWeek: StartingDayOfWeek.monday,
                        calendarController: _controller,
                        events: _events,
                        onDaySelected: (date, events,holidays) 
                          setState(() 
                            _selectedEvents = events;
                            _selectedDate = date;
                          );
                        ,
                      ),
                    )
                  ],
                ),
              ),

              Container(
                child:Padding(
                  padding: const EdgeInsets.only(top: 28.0),
                  child: Text("Kegiatan Anda",
                    style: CustomTextStlye.proxima_bold_16_black,),
                ),
              ),

              Container(
                child: _selectedEvents != null ? Column(
                  children: List.generate(_selectedEvents.length, (index) =>
                      Container(
                        padding: const EdgeInsets.all(8.0),
                        child: Container(
                          height: MediaQuery.of(context).size.height/15,
                          decoration: BoxDecoration(
                              border: Border(bottom: BorderSide(color: Color.fromRGBO(228, 228, 228, 1)))

                          ),
                          child:
                          Center(
                            child:
                                Container(child:
                                Row(
                                  children: [
                                    Padding(
                                      padding: const EdgeInsets.all(8.0),
                                      child: Container(
                                        padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 10.0),
                                        height: MediaQuery.of(context).size.height/10,
                                        decoration: BoxDecoration(
                                          border: Border.all(color:Color(0xFF29AAE1)),
                                          color:Color(0xFF29AAE1),
                                          borderRadius: BorderRadius.circular(3.0),
                                        ),
                                        child: Text(DateFormat('d').format(_selectedDate),
                                          style: CustomTextStlye.proxima_bold_18_white,
                                        ),
                                      ),
                                    ),

                                    Text(_selectedEvents[index],
                                      style: CustomTextStlye.proxima_bold_14_black,
                                    ),
                                  ],
                                ),
                                )

                          ),
                        ),
                      ),
                  ),
                ) : Container(),
              )
            ],
          ),
        ],
      ),
    );
  

这是我的 eventCalendarService.dart

import 'dart:convert';
import 'dart:io';

import 'package:http/io_client.dart';
import 'package:urus_flutter/data/eventController.dart';
import 'package:urus_flutter/data/model/base/event_calendar.dart';

Future<List<Event_Calendar>> fetchEventCalendar(String id, int company_id, String date) async 
  String requestBody = '';
  print(requestBody);

  final ioc = new HttpClient();
  ioc.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
  final http = new IOClient(ioc);
  final response = await http.get(
    getStringUrl+'staffs/GetCalendar?companyid=$company_id&month=$date',
  );

  print(response.statusCode);
  print(response.body);

  if (response.statusCode == 200) 
    var parsed = jsonDecode(response.body);
    return List<Event_Calendar>.from(parsed.map((model) => Event_Calendar.fromJson(model)));
   else 
    throw Exception(response.body);
  

这里是我的模型 event_calendar.dart

class Event_Calendar 
  final String id;
  final String type;
  final String date;
  final String event_name;
  final int company_id;


  Event_Calendar(
      
        this.id,
        this.type,
        this.date,
        this.event_name,
        this.company_id,
      
  );

  factory Event_Calendar.fromJson(Map<String, dynamic> json) 
    return Event_Calendar(
        id: json['id'] as String,
        type: json['type'] as String,
        date: json['date'] as String,
        event_name: json['event_name'] as String,
        company_id: json['company_id'] as int,
    );
  

我希望任何人都可以回答我如何在我的 API 中显示 _events。谢谢你。这是我的 API 中的示例值

【问题讨论】:

查看这个帖子***.com/questions/60276303/… 我试过了,但对我来说是错误的。结构完全不同T^T 【参考方案1】:

伙计们,现在我将向您展示我发现的使用表格日历显示来自 api 的事件和卡片的方式。 我什至不需要说这是我发现东西的方式,随意添加新东西并在这里给出提示。所以我们走吧。

首先我们将显示日历事件,但在这一步中只有标记,如果你在这里,来自 api 的数据必须包含日期,在我的情况下,日期是字符串,所以让我们创建模型给他们

import 'dart:convert';

class EventsModel 
  final String dataDoJob;
  EventsModel(
    required this.dataDoJob,
  );
  

  Map<String, dynamic> toMap() 
    return 
      'data_acao': dataDoJob,
    ;
  

  factory EventsModel.fromMap(Map<String, dynamic> map) 
    return EventsModel(
      dataDoJob: map['data_acao'],
    );
  

  String toJson() => json.encode(toMap());

  factory EventsModel.fromJson(String source) => EventsModel.fromMap(json.decode(source));

这是我的模型,你可以看到我只是得到日期。现在让我们使用 get 方法从 api 中检索这些数据,我使用的是 getConnect 但你可以使用你想要的 http 客户端。

 @override
  Future<List<EventsModel>> getEvents() async 
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();

    final int? id = sharedPreferences.getInt("idInfluencer");
    final String token = sharedPreferences.getString("token") ?? "";

    final Response result = await _restClient.get<List<EventsModel>>(
        "/job_acoes?influenciador_id=$id.toString()",
        headers: 
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Authorization': 'Bearer $token'
        , decoder: (data) 
      if (data != null) 
        return data
            .map<EventsModel>((event) => EventsModel.fromMap(event))
            .toList();
      
      return <EventsModel>[];
    );
    if (result.hasError) 
      print(result.statusCode);
      throw "Erro ao buscar dados";
    

    print(result.body);
    print(result.statusCode);

    return result.body;
  

做得好,我们已经有了一个日期列表,但在我的例子中,它们是字符串,所以我必须将它们转换,如下所示:

final events = await _jobsServices.getEvents();
        //final dateFormat = DateFormat("yyyy-MM-dd");
        final eventsConvert =
            events.map((date) => (DateTime.parse(date.dataDoJob))).toList();

        eventsList.assignAll(eventsConvert);

        print("Lista de eventos : $eventsList");

        streamController.add(events);

在第一行,我将列表保存在一个名为 events 的变量中,在下面我使用 map 方法将字符串转换为日期时间,并将它们添加到我创建的空列表中,然后逐步创建这个:创建一个空列表并将转换后的数据添加到上面,我的空列表称为 eventsList 完成后,我们将在表格日历中显示此列表

class CalendarWidget extends GetView<HomeController> 
  const CalendarWidget(Key? key) : super(key: key);
  @override
  Widget build(BuildContext context) 
    return StreamBuilder<List<EventsModel>>(
        stream: controller.streamController.stream,
        builder: (context, snapshot) 
          return Obx(() 
            return TableCalendar(
              eventLoader: (day) => controller.eventsList.where((event) => isSameDay(event,day)).toList(), //THIS IS IMPORTANT
       
              focusedDay: controller.focusedDay.value,
              firstDay: DateTime(2019),
              lastDay: DateTime(2050),
              headerStyle:
                  const HeaderStyle(formatButtonVisible: false), //WEEK VISIBLE
              locale: 'pt_BR',
              daysOfWeekVisible: true,
              calendarFormat: controller.format.value,
              onFormatChanged: (CalendarFormat _format) =>
                  controller.calendarFormat(_format),
              onDaySelected: (DateTime userSelectedDay, DateTime focusedDay) =>
                  controller.selectedDay(userSelectedDay, focusedDay),
              calendarStyle: CalendarStyle(
                  selectedTextStyle: const TextStyle(color: Colors.white),
                  isTodayHighlighted: true,
                  selectedDecoration: BoxDecoration(
                      color: context.buttomThemeClicled,
                      shape: BoxShape.circle)),
              selectedDayPredicate: (DateTime date) 
                return isSameDay(controller.focusedDay.value, date);
              ,
            );
          );
        );
  

记得我使用的是无状态小部件,所以我需要一个状态管理器,我使用 getx,所以它有一个涉及整个日历的 obx。

在事件加载器中有些人有疑问,您可以在其上传递列表或地图,在我的情况下,为了简单起见,我正在使用列表,请注意在事件加载器中我正在做一个简单的过滤,将我的日历日期与我从 api 获得的日期进行比较,不是吗?通过这样做,您的基于 api 的书签将已显示。啊,一个细节,流生成器用于在api发生变化时重做我的小部件,如果你不知道如何使用它,这个视频会解释:https://www.youtube.com/watch?v=BBelgajHgzY

现在让我们进入基于天数的事件显示部分,我的事件将显示在这样的卡片上:

所以我将它构建在与我家不同的页面上,这部分很重要,因为您的代码将更简单、更清晰,完成小部件后,我们将在屏幕上显示它们,如下所示:

child: Obx(() 
                          return ListView(
                            scrollDirection: Axis.vertical,
                            children: controller.cards
                                .map(
                                  (c) => AgendaCards(
                                    bottomPosition: 80,
                                    leftPositioned: 260,
                                    maxRadius: 5,
                                    rightPositioned: 5,
                                    secondMaxradius: 5,
                                    topPositioned: 20,
                                    model: c,
                                  ),
                                )
                                .toList(),
                          );
                        ));

名为日历卡片的小部件只不过是上面照片中的卡片,我在上面询问了一个名为

的模型
final JobsDescriptionCardsModel model;

并在构造函数中调用他

AgendaCards(
    required this.leftPositioned,
    required this.rightPositioned,
    required this.topPositioned,
    required this.bottomPosition,
    required this.maxRadius,
    required this.secondMaxradius,
    required this.model, //HERE
    Key? key,
  ) : super(key: key);

让我们创建这个模型

class JobsDescriptionCardsModel 
  final String descricaoJob;
  final String dataDoJob;
  final String horarioDoJob;
  final int jobId;
  final String nome;
  JobsDescriptionCardsModel(
    required this.descricaoJob,
    required this.dataDoJob,
    required this.horarioDoJob,
    required this.jobId,
    required this.nome,
  );
  

  Map<String, dynamic> toMap() 
    return 
      'descricaoJob': descricaoJob,
      'dataDoJob': dataDoJob,
      'horarioDoJob': horarioDoJob,
      'jobId': jobId,
      'nome': nome,
    ;
  

  factory JobsDescriptionCardsModel.fromMap(Map<String, dynamic> map) 
    return JobsDescriptionCardsModel(
      descricaoJob: map['descricao'] ?? "",
      dataDoJob: map['data_acao'] ?? "",
      horarioDoJob: map['hora_inicial_acao'],
      jobId: map['job_acao_id'] ?? 0,
      nome: map['job'] ["cliente"] ["nome"] ?? "",
    );
  

  String toJson() => json.encode(toMap());

  factory JobsDescriptionCardsModel.fromJson(String source) => JobsDescriptionCardsModel.fromMap(json.decode(source));

并在 api 上获取它

 @override
  Future<List<JobsDescriptionCardsModel>> getJobsDescrition() async 
    SharedPreferences sharedPreferences = await SharedPreferences.getInstance();

    final int? id = sharedPreferences.getInt("idInfluencer");
    final String token = sharedPreferences.getString("token") ?? "";

    final result = await _restClient.get<List<JobsDescriptionCardsModel>>(
        "/job_acoes?influenciador_id=$id.toString()",
        headers: 
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'Authorization': 'Bearer $token'
        , decoder: (data) 
      if (data != null) 
        return data
            .map<JobsDescriptionCardsModel>(
                (j) => JobsDescriptionCardsModel.fromMap(j))
            .toList();
      

      return <JobsDescriptionCardsModel>[];
    );

    if (result.hasError) 
      throw ("erro ao buscar dados");
    
    return result.body ?? <JobsDescriptionCardsModel>[];
  

api 给了我一个重要的列表。有了列表,我们将进入表格日历的概念。要继续并了解将要做什么,我建议您观看此视频:https://www.youtube.com/watch?v=HKuzPQUV21Y&t=291s

完成日历的配置后,相信您已经注意到,当您单击日期并打印具有日期数据的变量时,您会注意到表格日历为您提供了日期时间作为返回,并且如果你在我的模型中呈现了注意力,我也有一个来自 api 的日期,知道我们只需要根据表日历数据过滤来自 api 的列表,如下所示:

首先像我们之前一样创建一个空列表和一个将被过滤的列表:

  //LISTA DE JOBS CARDS

  final cards = <JobsDescriptionCardsModel>[].obs;

  //LISTA FILTRADA

  var cardsFiltered = <JobsDescriptionCardsModel>[];

空列表将被这样的 api 数据填充:

final jobsCards = await _jobsServices.getJobsDescrition();
        cards.assignAll(jobsCards);

我们将根据 API 日期过滤此列表,如下所示:

cardsFiltered = jobsCards;

        var novaLista = cardsFiltered.where((model) 
          return model.dataDoJob
              .toString()
              .contains(focusedDay.value.toString().substring(1, 10));
        );

看到首先我将填充的列表分配给一个新列表,然后我根据我的模型过滤了这个列表,仅在包含我字符串的日期的部分中,与我点击它时具有日期的变量进行比较记得吗?也转换为字符串,因为表日历为我提供了日期以及我认为是时间信息的其他数字,我仅从索引 1 到 10 获取数据,这将准确地为我提供变量中包含的日期。完成后,表格日历有一个名为 onDaySelected 的属性,它将显示我们过滤后的列表,如下所示:

selectedDay(DateTime selectedDayInfo, DateTime focusDayInfo) 
    userSelectedDay.value = selectedDayInfo;
    focusedDay.value = focusDayInfo;
    print(userSelectedDay.value);
    print(focusedDay.value);

    print("Lista de eventos 2 $eventsList");

    var novaLista = cardsFiltered.where((model) 
      return model.dataDoJob
          .toString()
          .contains(focusedDay.value.toString().substring(0, 10));
    );

    cards.assignAll(novaLista);

我在控制器中创建了这个单独的函数,并在表日历中调用它,如下所示:

onDaySelected: (DateTime userSelectedDay, DateTime focusedDay) =>
                  controller.selectedDay(userSelectedDay, focusedDay),

您的日历已经根据您构建的小部件显示默认标记和卡片,请记住使用您通过构造函数请求的模型将数据传递给您的卡片,因为它包含 api 数据。我希望我有帮助,我很抱歉谷歌翻译英语

【讨论】:

【参考方案2】:

_event 未在 table_calendar 的较新版本中使用。您可以使用eventLoader,它将在日历上显示您的活动。

例子:

日历小部件如下,

TableCalendar<Event>(
            firstDay: kFirstDay,
            lastDay: kLastDay,
            focusedDay: _focusedDay,
            selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
            eventLoader: _getEventsForDay,  // this will load events on calendar
            ),

_getEventsForDay方法如下,

 List<Event> _getEventsForDay(DateTime day) 
    return kEvents[day] ?? [];
  

kEvents(需要在日历上标注的事件列表)如下,如果有事件列表就不需要创建了。

final kEvents = LinkedHashMap<DateTime, List<Event>>(
  equals: isSameDay,
  hashCode: getHashCode,
)..addAll(_kEventSource);


final _kEventSource = Map.fromIterable(List.generate(50, (index) => index),
    key: (item) => DateTime.utc(kFirstDay.year, kFirstDay.month, item * 5),
    value: (item) => List.generate(
        item % 4 + 1, (index) => Event('Event $item | $index + 1')))
  ..addAll(
    kToday: [
      Event('Event 1'),
      Event('Event 2'),
    ],
  );

Event 类如下,(也可以随意制作)

class Event 
  final String title;

  const Event(this.title);

  @override
  String toString() => title;

希望你明白了!

【讨论】:

以上是关于如何在 Flutter 上使用我的 API 在表日历上显示事件的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Flutter 上使用 API

如何打印几个标记google map api 0.5.7 Flutter

如何使用 Dio 在 Flutter 中调用 API?

API 在 Flutter 的屏幕上返回 null

如何测试从 Flutter 应用程序到 localhost 服务的 api 调用?

如何使用dart / flutter在不同类上使用变量