작성
·
413
0
색상 상태관리 부분을 듣고 있습니다. 똑 같이 따라했는데요.
아래 내용과 같이 FutureBuilder를 사용하여 똑같이 따라 했는데도 불구하고 null이 리턴되어 오류가 납니다.
future: GetIt.I<LocalDatabase>().getCategoryColors() 에서 강사님 강의에서는 데이터를 가지고 오는데, 제가 만든 코드에서는 null이 리턴되네요. 혹시 몰라 main.dart에서 GetIt.I<LocalDatabase>().getCategoryColors() 를 실행해 보고, 결과를 보면 정상적인 데이터가 들어옵니다.
뭐가 문제일까요?
main.dart
import 'package:calendar_schedule_exam/database/drift_database.dart';
import 'package:calendar_schedule_exam/screen/home_screen.dart';
import 'package:drift/drift.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:intl/date_symbol_data_local.dart';
const DEFAULT_COLORS = [
// 빨강
'F44336',
// 주황
'FF9800',
// 노랑
'FFEB3B',
// 초록
'FCAF50',
// 파랑
'2196F3',
// 남
'3F51B5',
// 보라
'9C27B0',
];
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting();
final database = LocalDatabase();
GetIt.I.registerSingleton<LocalDatabase>(database);
final result = GetIt.I<LocalDatabase>().getCategoryColors();
final colors = await database.getCategoryColors();
if (colors.isEmpty) {
for (String hexCode in DEFAULT_COLORS) {
await database.createCategoryColor(
CategoryColorsCompanion(
hexCode: Value(hexCode),
),
);
}
}
runApp(MaterialApp(
theme: ThemeData(
fontFamily: 'NotoSans',
),
home: HomeScreen(),
));
}
schedule_bottom_sheet.dart
import 'package:calendar_schedule_exam/component/custom_text_field.dart';
import 'package:calendar_schedule_exam/const/colors.dart';
import 'package:calendar_schedule_exam/database/drift_database.dart';
import 'package:calendar_schedule_exam/model/category_color.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
class ScheduleBottomSheet extends StatefulWidget {
const ScheduleBottomSheet({super.key});
@override
State<ScheduleBottomSheet> createState() => _ScheduleBottomSheetState();
}
class _ScheduleBottomSheetState extends State<ScheduleBottomSheet> {
final GlobalKey<FormState> formKey = GlobalKey();
int? startTime;
int? endTime;
String? content;
int? selectedColorId;
@override
Widget build(BuildContext context) {
final bottomInset = MediaQuery.of(context).viewInsets.bottom;
return SafeArea(
bottom: true,
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
child: Container(
height: MediaQuery.of(context).size.height / 2 + bottomInset,
color: Colors.white,
child: Padding(
padding: EdgeInsets.only(bottom: bottomInset),
child: Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0, top: 16.0),
child: Form(
key: formKey,
autovalidateMode: AutovalidateMode.always,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_Time(
onStartSaved: (newValue) {
startTime = int.parse(newValue!);
},
onEndSaved: (newValue) {
endTime = int.parse(newValue!);
},
),
SizedBox(height: 16.0),
_Content(
onSaved: (newValue) {
content = newValue;
},
),
SizedBox(height: 16.0),
FutureBuilder<List<CategoryColor>>(
future: GetIt.I<LocalDatabase>().getCategoryColors(),
builder: (context, snapshot) {
print(snapshot.data);
if (snapshot.hasData &&
selectedColorId == null &&
snapshot.data!.isNotEmpty) {
selectedColorId = snapshot.data![0].id;
}
return _ColorPicker(
colors: snapshot.hasData ? snapshot.data! : [],
selectColorId: selectedColorId!,
);
}),
SizedBox(height: 16.0),
_SaveButton(
onPressed: onSavePressed,
),
],
),
),
),
),
),
),
);
}
void onSavePressed() {
if (formKey.currentState == null) {
return;
}
if (formKey.currentState!.validate()) {
formKey.currentState!.save();
} else {
print('에러가 있습니다.');
}
}
}
class _Time extends StatelessWidget {
final FormFieldSetter<String> onStartSaved;
final FormFieldSetter<String> onEndSaved;
const _Time(
{super.key, required this.onStartSaved, required this.onEndSaved});
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: CustomTextField(
label: '시작 시간',
isTime: true,
onSaved: onStartSaved,
)),
SizedBox(width: 16.0),
Expanded(
child: CustomTextField(
label: '마감 시간',
isTime: true,
onSaved: onEndSaved,
),
),
],
);
}
}
class _Content extends StatelessWidget {
final FormFieldSetter<String> onSaved;
const _Content({super.key, required this.onSaved});
@override
Widget build(BuildContext context) {
return Expanded(
child: CustomTextField(
label: '내용',
isTime: false,
onSaved: onSaved,
),
);
}
}
class _ColorPicker extends StatelessWidget {
final List<CategoryColor> colors;
final int selectColorId;
const _ColorPicker(
{super.key, required this.colors, required this.selectColorId});
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 8.0,
runSpacing: 10.0,
children: colors.map((e) => rendColor(e, selectColorId == e.id)).toList(),
);
}
Widget rendColor(CategoryColor color, bool isSelected) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(int.parse('FF${color.hexCode}', radix: 16)),
border:
isSelected ? Border.all(color: Colors.black, width: 1.0) : null,
),
height: 32,
width: 32);
}
}
class _SaveButton extends StatelessWidget {
final VoidCallback onPressed;
const _SaveButton({super.key, required this.onPressed});
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: PRIMARY_COLOR,
),
onPressed: onPressed,
child: Text('Save'),
),
);
}
}
drift_database.dart
import 'dart:io';
import 'package:calendar_schedule_exam/model/category_color.dart';
import 'package:calendar_schedule_exam/model/schedule.dart';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
part 'drift_database.g.dart';
@DriftDatabase(
tables: [
Schedules,
CategoryColors,
],
)
class LocalDatabase extends _$LocalDatabase {
LocalDatabase() : super(_openConnection());
// insert schedules
Future<int> createSchedule(SchedulesCompanion data) => into(schedules).insert(data);
// insert categoryColors
Future<int> createCategoryColor(CategoryColorsCompanion data) => into(categoryColors).insert(data);
// select all categoryColors
Future<List<CategoryColor>> getCategoryColors() => select(categoryColors).get();
@override
// TODO: implement schemaVersion
int get schemaVersion => 1;
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase(file);
});
}
오류내용
Launching lib/main.dart on iPhone 14 Pro Max in debug mode...
Running Xcode build...
Xcode build done. 7.8s
[VERBOSE-2:FlutterDarwinContextMetalImpeller.mm(35)] Using the Impeller rendering backend.
Debug service listening on ws://127.0.0.1:61505/HeTRp4ZQphA=/ws
Syncing files to device iPhone 14 Pro Max...
flutter: null
======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building FutureBuilder<List<CategoryColor>>(dirty, state: _FutureBuilderState<List<CategoryColor>>#41541):
Null check operator used on a null value
The relevant error-causing widget was:
FutureBuilder<List<CategoryColor>> FutureBuilder:file:///Users/choiwooin/dev/flutterProject/calendar_schedule_exam/lib/component/schedule_bottom_sheet.dart:57:21
When the exception was thrown, this was the stack:
#0 _ScheduleBottomSheetState.build.<anonymous closure> (package:calendar_schedule_exam/component/schedule_bottom_sheet.dart:68:59)
#1 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:612:55)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:5198:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5086:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11)
#5 Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#6 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5068:5)
#7 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5242:11)
와 저도 앱삭제하고 다시하니 문제가 해결됬어요! 감사합니다.