在 syncfusion_calendar 之上的 Flutter CircularProgressIndicator

问题描述

最近几天我一直在为正确的 futurebuilder 定位而苦苦挣扎。我使用 syncfusion_calendar 包来显示来自我的 API 的 json 数据,每次用户更改日历月时我都会调用一个新的请求到 API。问题是用户没有被告知正在进行的数据下载,我很乐意通过在加载时显示 CircularProgressIndicator 而不是日历来做到这一点。

我的 pubspec 文件以防万一:

name: Flutter_viaapp_startmenu
description: A new Flutter application.

# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots,like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in Flutter
# build by specifying --build-name and --build-number,respectively.
# In Android,build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS,build-name is used as CFBundleShortVersionString while build-number used as CFBundLeversion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  firebase_core: ^0.5.0+1
  cloud_firestore: ^0.14.4
  firebase_messaging: ^7.0.3
  firebase_in_app_messaging: 0.2.3
  webview_Flutter: ^1.0.7
  Flutter_staggered_grid_view: ^0.3.3
  easy_localization: ^2.3.2
  intl: ^0.16.1
  http: ^0.12.2
  syncfusion_Flutter_calendar: ^18.3.51
  shared_preferences: ^0.5.12
  Flutter_local_notifications: ^3.0.3

  Flutter_localizations:
    sdk: Flutter
  Flutter:
    sdk: Flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.0

dev_dependencies:
  Flutter_test:
    sdk: Flutter

# For information on the generic Dart part of this file,see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
Flutter:
  # The following line ensures that the Material Icons font is
  # included with your application,so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application,add an assets section,like this:
  assets:
    - assets/menu.jpg
    - assets/welcome.jpg
    - assets/translations/en.json
    - assets/translations/lv.json

  # An image asset can refer to one or more resolution-specific "variants",see
  # https://Flutter.dev/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies,see
  # https://Flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application,add a fonts section here,# in this "Flutter" section. Each entry in this list should have a
  # "family" key with the font family name,and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,# see https://Flutter.dev/custom-fonts/#from-packages

我的主日历文件

import 'dart:async';
import 'dart:convert';

import 'package:Flutter/cupertino.dart';
import 'package:Flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:syncfusion_Flutter_calendar/calendar.dart';
import 'package:intl/intl.dart';

class Lecture_graph extends StatefulWidget {
  Lecture_graph({Key key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => _MyLecturesGraphState();
}

class _MyLecturesGraphState extends State<Lecture_graph> {
  Future<List<Lecture>> _future;
  List<Lecture> lectures;
  DateTime _selectedDate = new DateTime.Now();
  List<LectureTime> _times;

  //Todo make this empty after SO post
  var coursecode = "IT3";

  @override
  void initState() {
    _selectedDate = new DateTime(
        _selectedDate.year,_selectedDate.month,15,_selectedDate.hour,_selectedDate.minute,_selectedDate.second,_selectedDate.millisecond,_selectedDate.microsecond);
    _future = downloadData();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(coursecode + " | Lectures"),actions: [
            LecturesNavigationControls(),],),body: lectureGraphList());
  }

  Future<String> _checkSavedCourse() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();

    String _coursecode = prefs.getString('savedCourse');

    if (_coursecode == "" || _coursecode == null) {
      Navigator.push(
        context,MaterialPageRoute(builder: (context) => CourseSelectionPage()),);
      return null;
    } else {
      return _coursecode;
    }
  }

  Widget lectureGraphList() {
    return FutureBuilder<List<Lecture>>(
      future: _future,builder: (BuildContext context,AsyncSnapshot<List<Lecture>> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(
            child: SizedBox(
              child: const Expanded(
                  child: Center(child: const CircularProgressIndicator())),width: 100,height: 100,);
        } else {
          if (snapshot.hasError)
            return Center(child: Text('Error: ${snapshot.error}'));
          else
            return showLectures(lectures);
        }
      },);
  }


//THIS FUNCTION IS CALLED EVERY TIME TO DOWNLOAD NEW DATA FROM API

  Future<List<Lecture>> downloadData() async {
    //get saved course
    if (coursecode == "" || coursecode == null) {
      coursecode = await _checkSavedCourse();
      //debug
      print('courscode recieved from sharedprefs');
    }

    //if there is no date selected,select today
    if (_selectedDate == null) _selectedDate = new DateTime.Now();

    //build request URL
    var requestURL =
        'https://lekcijas.va.lv/lekcijas_android/getMonthLectures.PHP?date=' +
            DateFormat('yyyy-MM').format(_selectedDate) +
            "&breaks&program=" +
            coursecode;
    //wait for response
    var response = await http.get(requestURL);
    var data = json.decode(response.body)["result"];

    //clear array after each request
    if (lectures != null) lectures.clear();

    try {
      //create lectures from json response
      lectures = List<Lecture>.from(data.map((x) => Lecture.fromJson(x)));
      _getDataSource(lectures);
    } catch (e) {
      print(e.toString());
    }

    return Future.value(lectures);
  }

  Widget showLectures(List<Lecture> lectures) {
    return Card(
      child: Row(
        children: [
          Expanded(
              child: SfCalendar(
                  view: CalendarView.month,firstDayOfWeek: 1,onViewChanged: (ViewChangedDetails details) {
                    if (_selectedDate.month != details.visibleDates[15].month) {
                      WidgetsBinding.instance.addPostFrameCallback((_) {
                        _selectedDate = details.visibleDates[15];


                          setState(() {



//CALENDAR MONTH CHANGE IS CALLED HERE 



                              downloadData();
                

        });
                      });
                    }
                  },dataSource: LectureTimeDataSource(_times),monthViewSettings: MonthViewSettings(
                      appointmentdisplayMode:
                          MonthAppointmentdisplayMode.indicator,showAgenda: true,agendaStyle: AgendaStyle(
                          appointmentTextStyle:
                              TextStyle(color: Colors.black))),showNavigationArrow: true))
        ],);
  }

  void _getDataSource(List<Lecture> lectures) {
    var lectureTimes = <LectureTime>[];
    lectures.forEach((element) {
      lectureTimes.add(LectureTime(
          (element.classroom + "  " + element.lecture),DateTime.parse(element.datums + " " + element.start),DateTime.parse(element.datums + " " + element.end),hexToColor(element.color),false));
    });

    setState(() {
      _times = lectureTimes;
    });
  }
}

class LecturesNavigationControls extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        IconButton(
          //Todo find normal icon
          icon: const Icon(Icons.wheelchair_pickup),onpressed: () {
            Navigator.push(
              context,);
          },);
  }
}

class LectureTimeDataSource extends CalendarDataSource {
  LectureTimeDataSource(List<LectureTime> source) {
    appointments = source;
  }

  @override
  DateTime getStartTime(int index) {
    return appointments[index].from;
  }

  @override
  DateTime getEndTime(int index) {
    return appointments[index].to;
  }

  @override
  String getSubject(int index) {
    return appointments[index].eventName;
  }

  @override
  Color getColor(int index) {
    return appointments[index].background;
  }

  @override
  bool isAllDay(int index) {
    return appointments[index].isAllDay;
  }
}

class LectureTime {
  LectureTime(
      this.eventName,this.from,this.to,this.background,this.isAllDay);

  String eventName;
  DateTime from;
  DateTime to;
  Color background;
  bool isAllDay;
}

Color hexToColor(String code) {
  return new Color(int.parse(code.substring(1,7),radix: 16) + 0xFF000000);
}


class Lecture{
  final String programs;
  final String lecture;
  final String lecturer;
  final String start;
  final String end;
  final String classroom;
  final String color;
  final String datums;

  Lecture({this.programs,this.lecture,this.lecturer,this.start,this.end,this.classroom,this.color,this.datums});

  factory Lecture.fromJson(Map<String,dynamic> json) {
    return Lecture(
      programs: json['nodala'] as String,lecture: json['kuRSS'] as String,lecturer : json['lektors'] as String,start: json['sakums'] as String,end: json['beigas'] as String,classroom: json['nosaukums'] as String,color: json['iela'] as String,datums: json['datums'] as String,);
  }
}


class LectureTime {
  LectureTime(
      this.eventName,this.isAllDay);

  String eventName;
  DateTime from;
  DateTime to;
  Color background;
  bool isAllDay;
}

请使用“IT3”字符串作为 API 的课程代码。 API 请求网址示例 here

解决方法

您可以添加一个名为downloadingData 的变量并将其默认设置为false。然后,在调用 downloadData() 之前将其设置为 true,当函数完成时,将其设置回 false。 最后,在 build 方法中: 孩子:正在下载数据? CircularProgressIndicator() : SfCalendar(...)

,

根据提供的信息和代码片段,我们进行了检查,您的要求是“在加载日历时显示 CircularProgressIndicator”。我们准备了一个简单的示例,用于加载循环进度指示器并将在线数据加载到日历。请从以下链接中找到示例,

示例链接: https://www.syncfusion.com/downloads/support/directtrac/312448/ze/minimum_appointmentduration298839661.zip

此外,我们有一个 KB 文档,用于通过简单的加载文本消息将在线数据加载到 Flutter 日历。同样,您将使用 CircularProgressIndicator。

知识库链接: https://www.syncfusion.com/kb/11568/how-to-load-the-json-data-online-to-the-flutter-event-calendar-sfcalendar-appointments

我们希望以上示例和知识库对您有所帮助。如果您需要进一步的帮助,请告诉我们。