Merge branch '8-datenbank-einbinden' into 'main'
Resolve "Datenbank einbinden" Closes #8 See merge request Crondung/hsma_cpd!10main
commit
82296ce0e5
|
@ -1,3 +1,5 @@
|
||||||
abstract class DatabaseRecord {
|
abstract class DatabaseRecord {
|
||||||
String toCSV();
|
String toCSV();
|
||||||
|
Map<String, dynamic> toMap();
|
||||||
|
DatabaseRecord.fromDatabase(Map<String, dynamic> map);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:smoke_cess_app/pages/main_page.dart';
|
import 'package:smoke_cess_app/pages/main_page.dart';
|
||||||
|
import 'package:smoke_cess_app/service/database_service.dart';
|
||||||
import 'package:smoke_cess_app/service/notification_service.dart';
|
import 'package:smoke_cess_app/service/notification_service.dart';
|
||||||
import 'package:timezone/data/latest.dart' as tz;
|
import 'package:timezone/data/latest.dart' as tz;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// to ensure all the widgets are initialized.
|
// to ensure all the widgets are initialized.
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
//init database
|
||||||
|
DatabaseService.instance;
|
||||||
tz.initializeTimeZones();
|
tz.initializeTimeZones();
|
||||||
NotificationService().initNotification();
|
NotificationService().initNotification();
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
|
@ -19,6 +21,6 @@ class MyApp extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(title: _title, home: MyHomePage());
|
return const MaterialApp(title: _title, home: MyHomePage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,32 @@
|
||||||
import 'package:smoke_cess_app/interface/db_record.dart';
|
import 'package:smoke_cess_app/interface/db_record.dart';
|
||||||
|
|
||||||
class HIITWorkout implements DatabaseRecord {
|
class HIITWorkout implements DatabaseRecord {
|
||||||
final Duration _workoutDuration;
|
Duration _workoutDuration;
|
||||||
final String _commentBefore;
|
String _commentBefore;
|
||||||
final String _commentAfter;
|
String _commentAfter;
|
||||||
final DateTime _workoutDate;
|
DateTime _workoutDate;
|
||||||
|
|
||||||
HIITWorkout(this._workoutDuration, this._commentBefore, this._commentAfter,
|
HIITWorkout(this._workoutDuration, this._commentBefore, this._commentAfter,
|
||||||
this._workoutDate);
|
this._workoutDate);
|
||||||
|
|
||||||
|
//TODO Felder anpassen
|
||||||
|
@override
|
||||||
|
factory HIITWorkout.fromMap(Map<String, dynamic> map) {
|
||||||
|
return HIITWorkout(map['_workoutDuration'], map['_commentBefore'],
|
||||||
|
map['_commentAfter'], map['_workoutDate']);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toCSV() =>
|
String toCSV() =>
|
||||||
"${_workoutDate.toIso8601String()}, $_workoutDuration, $_commentBefore, $_commentAfter";
|
"${_workoutDate.toIso8601String()}, $_workoutDuration, $_commentBefore, $_commentAfter";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'workoutDuration': _workoutDuration,
|
||||||
|
'commentBefore': _commentBefore,
|
||||||
|
'commentAfter': _commentAfter,
|
||||||
|
'workoutDate': _workoutDate,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,29 @@
|
||||||
import 'package:smoke_cess_app/interface/db_record.dart';
|
import 'package:smoke_cess_app/interface/db_record.dart';
|
||||||
|
|
||||||
class Mood implements DatabaseRecord {
|
class Mood implements DatabaseRecord {
|
||||||
final double _moodValue;
|
final int _moodValue;
|
||||||
final String _comment;
|
final String _comment;
|
||||||
final DateTime _date;
|
final DateTime _date;
|
||||||
|
|
||||||
Mood(this._moodValue, this._comment, this._date);
|
Mood(this._moodValue, this._comment, this._date);
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory Mood.fromDatabase(Map<String, dynamic> map) {
|
||||||
|
DateTime date = DateTime.parse(map['date']);
|
||||||
|
return Mood(map['value'], map['comment'], date);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toCSV() {
|
String toCSV() {
|
||||||
return "${_date.toIso8601String()}, ${_moodValue.round().toString()}, $_comment";
|
return "${_date.toIso8601String()}, $_moodValue, $_comment";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'value': _moodValue,
|
||||||
|
'comment': _comment,
|
||||||
|
'date': _date.toIso8601String(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:smoke_cess_app/interface/db_record.dart';
|
import 'package:smoke_cess_app/interface/db_record.dart';
|
||||||
|
|
||||||
class Sleep implements DatabaseRecord {
|
class Sleep implements DatabaseRecord {
|
||||||
final double _sleepQualityValue;
|
final int _sleepQualityValue;
|
||||||
final String _comment;
|
final String _comment;
|
||||||
final DateTime _date;
|
final DateTime _date;
|
||||||
final TimeOfDay _sleepedAt;
|
final TimeOfDay _sleepedAt;
|
||||||
|
@ -11,8 +11,31 @@ class Sleep implements DatabaseRecord {
|
||||||
Sleep(this._sleepQualityValue, this._comment, this._date, this._sleepedAt,
|
Sleep(this._sleepQualityValue, this._comment, this._date, this._sleepedAt,
|
||||||
this._wokeUpAt);
|
this._wokeUpAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory Sleep.fromDatabase(Map<String, dynamic> map) {
|
||||||
|
DateTime date = DateTime.parse(map['date']);
|
||||||
|
TimeOfDay sleepedAt =
|
||||||
|
TimeOfDay(hour: map['sleepedAtHour'], minute: map['sleepedAtMinute']);
|
||||||
|
TimeOfDay wokeUpAt =
|
||||||
|
TimeOfDay(hour: map['wokeUpAtHour'], minute: map['wokeUpAtMinute']);
|
||||||
|
return Sleep(map['value'], map['comment'], date, sleepedAt, wokeUpAt);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toCSV() {
|
String toCSV() {
|
||||||
return "${_date.toIso8601String()}, ${_sleepQualityValue.round().toString()}, $_sleepedAt, $_wokeUpAt, $_comment";
|
return "${_date.toIso8601String()}, $_sleepQualityValue, ${_sleepedAt.hour}:${_sleepedAt.minute}, ${_wokeUpAt.hour}:${_wokeUpAt.minute}, $_comment";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'value': _sleepQualityValue,
|
||||||
|
'comment': _comment,
|
||||||
|
'date': _date.toIso8601String(),
|
||||||
|
'sleepedAtHour': _sleepedAt.hour,
|
||||||
|
'sleepedAtMinute': _sleepedAt.minute,
|
||||||
|
'wokeUpAtHour': _wokeUpAt.hour,
|
||||||
|
'wokeUpAtMinute': _wokeUpAt.minute,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||||
|
import 'package:smoke_cess_app/models/mood.dart';
|
||||||
import 'package:smoke_cess_app/models/settings.dart';
|
import 'package:smoke_cess_app/models/settings.dart';
|
||||||
|
import 'package:smoke_cess_app/service/database_service.dart';
|
||||||
import 'package:smoke_cess_app/service/json_service.dart';
|
import 'package:smoke_cess_app/service/json_service.dart';
|
||||||
import 'package:smoke_cess_app/service/settings_service.dart';
|
import 'package:smoke_cess_app/service/settings_service.dart';
|
||||||
import 'package:smoke_cess_app/service/notification_service.dart';
|
import 'package:smoke_cess_app/service/notification_service.dart';
|
||||||
|
|
||||||
|
import '../models/sleep.dart';
|
||||||
import '../widgets/missing_config_popup.dart';
|
import '../widgets/missing_config_popup.dart';
|
||||||
|
|
||||||
class ScannerPage extends StatefulWidget {
|
class ScannerPage extends StatefulWidget {
|
||||||
|
@ -71,6 +74,23 @@ class ScannerPageState extends State<ScannerPage> {
|
||||||
NotificationService().setAllNotifications();
|
NotificationService().setAllNotifications();
|
||||||
},
|
},
|
||||||
child: const Text('Read JSON'),
|
child: const Text('Read JSON'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
textStyle: const TextStyle(fontSize: 20)),
|
||||||
|
onPressed: () async {
|
||||||
|
List<Mood> moods = await DatabaseService.instance.getMoodRecords();
|
||||||
|
List<Sleep> sleeps =
|
||||||
|
await DatabaseService.instance.getSleepRecords();
|
||||||
|
for (Mood mood in moods) {
|
||||||
|
print(mood.toCSV());
|
||||||
|
}
|
||||||
|
for (Sleep sleep in sleeps) {
|
||||||
|
print(sleep.toCSV());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('Export'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:path/path.dart';
|
||||||
|
import 'package:smoke_cess_app/models/mood.dart';
|
||||||
|
import 'package:sqflite/sqflite.dart';
|
||||||
|
// ignore: depend_on_referenced_packages
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import '../models/sleep.dart';
|
||||||
|
|
||||||
|
class DatabaseService {
|
||||||
|
DatabaseService._privateConstructor();
|
||||||
|
static final DatabaseService instance = DatabaseService._privateConstructor();
|
||||||
|
|
||||||
|
static Database? _database;
|
||||||
|
Future<Database> get database async => _database ??= await _initDatabase();
|
||||||
|
|
||||||
|
Future<Database> _initDatabase() async {
|
||||||
|
Directory documentsDirectory = await getApplicationDocumentsDirectory();
|
||||||
|
String path = join(documentsDirectory.path, 'database.db');
|
||||||
|
return await openDatabase(path,
|
||||||
|
version: 1, onCreate: _onCreate, onOpen: _createTablesIfNotExists);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _onCreate(Database db, int version) async {
|
||||||
|
await _createTablesIfNotExists(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _createTablesIfNotExists(Database db) async {
|
||||||
|
await db.execute(_createMoodTable);
|
||||||
|
await db.execute(_createSleepTable);
|
||||||
|
await db.execute(_createRelapseTable);
|
||||||
|
await db.execute(_createWorkoutTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO use generic function?
|
||||||
|
Future<List<Mood>> getMoodRecords() async {
|
||||||
|
Database db = await instance.database;
|
||||||
|
var moodRecords = await db.query('mood');
|
||||||
|
List<Mood> moodList = moodRecords.isNotEmpty
|
||||||
|
? moodRecords.map((e) => Mood.fromDatabase(e)).toList()
|
||||||
|
: [];
|
||||||
|
return moodList;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Sleep>> getSleepRecords() async {
|
||||||
|
Database db = await instance.database;
|
||||||
|
var sleepRecords = await db.query('sleep');
|
||||||
|
List<Sleep> sleepList = sleepRecords.isNotEmpty
|
||||||
|
? sleepRecords.map((e) => Sleep.fromDatabase(e)).toList()
|
||||||
|
: [];
|
||||||
|
return sleepList;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> addMood(Mood mood) async {
|
||||||
|
Database db = await instance.database;
|
||||||
|
return await db.insert('mood', mood.toMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> addSleep(Sleep sleep) async {
|
||||||
|
Database db = await instance.database;
|
||||||
|
return await db.insert('sleep', sleep.toMap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _createMoodTable = '''
|
||||||
|
CREATE TABLE IF NOT EXISTS mood(
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
value INTEGER,
|
||||||
|
date TEXT,
|
||||||
|
comment TEXT
|
||||||
|
)
|
||||||
|
''';
|
||||||
|
|
||||||
|
String _createSleepTable = '''
|
||||||
|
CREATE TABLE IF NOT EXISTS sleep(
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
value INTEGER,
|
||||||
|
date TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
sleepedAtHour INTEGER,
|
||||||
|
sleepedAtMinute INTEGER,
|
||||||
|
wokeUpAtHour INTEGER,
|
||||||
|
wokeUpAtMinute INTEGER
|
||||||
|
)
|
||||||
|
''';
|
||||||
|
|
||||||
|
String _createRelapseTable = '''
|
||||||
|
CREATE TABLE IF NOT EXISTS relapse(
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
date TEXT,
|
||||||
|
comment TEXT,
|
||||||
|
reason TEXT
|
||||||
|
)
|
||||||
|
''';
|
||||||
|
|
||||||
|
String _createWorkoutTable = '''
|
||||||
|
CREATE TABLE IF NOT EXISTS workout(
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
date TEXT,
|
||||||
|
motivationBefore INTEGER,
|
||||||
|
commentBefore TEXT,
|
||||||
|
motivationAfter INTEGER,
|
||||||
|
commentAfter TEXT,
|
||||||
|
completed INTEGER
|
||||||
|
)
|
||||||
|
''';
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smoke_cess_app/models/mood.dart';
|
||||||
|
import 'package:smoke_cess_app/service/database_service.dart';
|
||||||
import 'package:smoke_cess_app/widgets/slider.dart';
|
import 'package:smoke_cess_app/widgets/slider.dart';
|
||||||
import 'package:smoke_cess_app/widgets/submit_form_button.dart';
|
import 'package:smoke_cess_app/widgets/submit_form_button.dart';
|
||||||
import 'package:smoke_cess_app/widgets/text_formfield.dart';
|
import 'package:smoke_cess_app/widgets/text_formfield.dart';
|
||||||
|
@ -20,9 +22,9 @@ class _MoodFormState extends State<MoodForm> {
|
||||||
void submitForm() {
|
void submitForm() {
|
||||||
if (_moodFormKey.currentState!.validate()) {
|
if (_moodFormKey.currentState!.validate()) {
|
||||||
_moodFormKey.currentState?.save(); //call every onSave Method
|
_moodFormKey.currentState?.save(); //call every onSave Method
|
||||||
//TODO Businesslogik aufrufen!
|
Mood mood =
|
||||||
print(_textInput);
|
Mood(slider.getSliderValue().toInt(), _textInput, DateTime.now());
|
||||||
print(slider.sliderValue);
|
DatabaseService.instance.addMood(mood);
|
||||||
_moodFormKey.currentState?.reset();
|
_moodFormKey.currentState?.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smoke_cess_app/models/sleep.dart';
|
||||||
|
import 'package:smoke_cess_app/service/database_service.dart';
|
||||||
import 'package:smoke_cess_app/widgets/elevated_card.dart';
|
import 'package:smoke_cess_app/widgets/elevated_card.dart';
|
||||||
import 'package:smoke_cess_app/widgets/slider.dart';
|
import 'package:smoke_cess_app/widgets/slider.dart';
|
||||||
import 'package:smoke_cess_app/widgets/submit_form_button.dart';
|
import 'package:smoke_cess_app/widgets/submit_form_button.dart';
|
||||||
|
@ -26,10 +28,13 @@ class _SleepFormState extends State<SleepForm> {
|
||||||
void submitForm() {
|
void submitForm() {
|
||||||
if (_sleepFormKey.currentState!.validate()) {
|
if (_sleepFormKey.currentState!.validate()) {
|
||||||
_sleepFormKey.currentState?.save(); //call every onSave Method
|
_sleepFormKey.currentState?.save(); //call every onSave Method
|
||||||
//TODO Businesslogik aufrufen!
|
Sleep sleep = Sleep(
|
||||||
print(_textInput);
|
slider.getSliderValue().toInt(),
|
||||||
print(slider.sliderValue);
|
_textInput,
|
||||||
print('Eingeschlafen um: ${sleepTimePicker.getCurrentTime}');
|
DateTime.now(),
|
||||||
|
sleepTimePicker.getCurrentTime,
|
||||||
|
wakeUpTimePicker.getCurrentTime);
|
||||||
|
DatabaseService.instance.addSleep(sleep);
|
||||||
_sleepFormKey.currentState?.reset();
|
_sleepFormKey.currentState?.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
pubspec.lock
27
pubspec.lock
|
@ -234,14 +234,14 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.2"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
|
@ -371,6 +371,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
sqflite:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.4+1"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2+2"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -392,6 +406,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -457,4 +478,4 @@ packages:
|
||||||
version: "6.1.0"
|
version: "6.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.2 <3.0.0"
|
dart: ">=2.18.2 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.3.0"
|
||||||
|
|
|
@ -3,7 +3,7 @@ description: A new Flutter project.
|
||||||
|
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
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.
|
# The following defines the version and build number for your application.
|
||||||
# A version number is three numbers separated by dots, like 1.2.43
|
# A version number is three numbers separated by dots, like 1.2.43
|
||||||
|
@ -20,7 +20,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.18.2 <3.0.0"
|
sdk: '>=2.18.2 <3.0.0'
|
||||||
|
|
||||||
# Dependencies specify other packages that your package needs in order to work.
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
@ -31,6 +31,9 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
sqflite:
|
||||||
|
path:
|
||||||
|
path_provider: ^2.0.12
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
|
Loading…
Reference in New Issue