GraphQL을 활용한 Flutter CRUD 앱 템플릿 구현 가이드
GraphQL을 사용한 Flutter CRUD 앱 예제
소개
첫 번째 Flutter-GraphQL 가이드에서는 GraphQL API를 사용하여 Back4App에서 간단한 데이터에 연결하고 쿼리하는 Flutter 프로젝트를 설정하는 방법을 배웠습니다. 또한, Back4App GraphQL Playground를 사용하여 쿼리/변경 작업을 실행하여 앱 데이터베이스를 생성/채우는 방법도 배웠습니다.
이 튜토리얼에서는 GraphQL 변형을 사용하여 앱에서 Back4App 백엔드에서 직접 데이터를 생성, 업데이트 및 삭제하는 기능을 추가할 것입니다.
목표
이 기사의 끝에서 여러분이 할 수 있기를 기대하는 것은:
- GraphQL API를 사용하여 백엔드에 데이터를 생성합니다.
- GraphQL API를 사용하여 백엔드에서 데이터를 업데이트합니다.
- GraphQL API를 사용하여 백엔드에서 기존 데이터를 삭제합니다.

전제 조건
이 튜토리얼을 완료하려면 다음이 필요합니다:
- Android Studio 또는 VS Code와 같은 Flutter 코드를 작성할 수 있는 IDE.
참고: Back4App GraphQL API에 대한 더 나은 이해를 원하신다면 GraphQL 요리책을 확인하고 API 플레이그라운드에서 GraphQL 스키마도 확인하세요.
1 - GUI 설정하기
새 파일을 생성하세요 mutation_page.dart. 여기서 우리는 사용자로부터 입력을 받고 변이 작업을 수행하기 위한 GUI를 생성할 것입니다. 이제 다음 코드를 mutation_page.dart에 붙여넣으세요.
1 import 'package:back4appgraphqldemo/app_ui.dart';
2 import 'package:back4appgraphqldemo/database_utils.dart';
3 import 'package:flutter/material.dart';
4
5 class MutationPage extends StatelessWidget {
6
7 String langName,saveFormat,objectId;
8 DatabaseUtils utils;
9
10 @override
11 Widget build(BuildContext context) {
12 return AppUI(
13 onChangedName: (text){
14 langName=text;
15 },
16 onChangedSaveFormat: (text){
17 saveFormat=text;
18 },
19 onChangedObjectId: (text){
20 objectId=text;
21 },
22 sendDataButtonPressed: (){
23 },
24 deleteDataButtonPressed: (){
25 },
26 updateButtonPressed: (){
27 },
28 );
29 }
30 }AppUI() 위젯은 이미 만들어졌습니다. 그래서 우리는 MutationPage라는 이름의 클래스를 생성하고 AppUI 위젯을 반환했습니다. 또한 텍스트 필드에 대한 콜백 함수를 초기화하여 첫 번째 텍스트 필드의 텍스트는 langName, 두 번째는 saveFormat에 저장되고 마지막으로 objectId에 저장됩니다. 이제 main.dart로 진행하여 floatingActionButton 매개변수를 Scaffold() 위젯의 MyHomePage 클래스에 추가하고 다음 코드를 전달하세요:
1 floatingActionButton: Row(
2 mainAxisAlignment: MainAxisAlignment.end,
3 children: [
4 FloatingActionButton(
5 heroTag: 'mutation_page',
6 child: Text('M',
7 style: TextStyle(
8 color: Colors.white,
9 ),
10 ),
11 onPressed: (){
12 Navigator.pushReplacement(context, MaterialPageRoute(
13 builder: ((context){
14 return MutationPage();
15 })
16 ));
17 },
18 ),
19 ],
20 ),이것은 우리가 홈 페이지에서 MutationPage()로 이동할 Floating Button을 생성합니다. 이제 우리의 GUI가 설정되었습니다. 이제 앱을 Hot Restart하여 GUI를 확인할 수 있습니다. 당신의 main.dart는 다음과 같아야 합니다:
1 import 'package:back4appgraphqldemo/mutation_page.dart';
2 import 'package:flutter/material.dart';
3 import 'package:graphql_flutter/graphql_flutter.dart';
4 import 'consonents.dart';
5 import 'dart:ui';
6
7 void main() {
8 runApp(MyApp());
9 }
10
11 class MyApp extends StatelessWidget {
12 @override
13 Widget build(BuildContext context) {
14 final HttpLink httpLink = HttpLink(
15 uri: 'https://parseapi.back4app.com/graphql',
16 headers: {
17 'X-Parse-Application-Id': kParseApplicationId,
18 'X-Parse-Client-Key': kParseClientKey,
19 'X-Parse-Master-Key': kParseMasterKey,
20 //'X-Parse-REST-API-Key' : kParseRestApiKey,
21 }, //getheaders()
22 );
23
24 ValueNotifier<GraphQLClient> client = ValueNotifier(
25 GraphQLClient(
26 cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
27 link: httpLink,
28 ),
29 );
30
31 return MaterialApp(
32 home: GraphQLProvider(
33 child: MyHomePage(),
34 client: client,
35 ),
36 );
37 }
38 }
39
40 class MyHomePage extends StatefulWidget {
41 @override
42 _MyHomePageState createState() => _MyHomePageState();
43 }
44
45 class _MyHomePageState extends State<MyHomePage> {
46 String name;
47 String saveFormat;
48 String objectId;
49
50 String query = '''
51 query FindLanguages{
52 languages{
53 count,
54 edges{
55 node{
56 name,
57 saveFormat
58 }
59 }
60 }
61 }
62 ''';
63
64 @override
65 Widget build(BuildContext context) {
66 return SafeArea(
67 child: Scaffold(
68 appBar: AppBar(
69 title: Text(
70 'Parsing data using GraphQL',
71 ),
72 ),
73 floatingActionButton: Row(
74 mainAxisAlignment: MainAxisAlignment.end,
75 children: [
76 FloatingActionButton(
77 heroTag: 'mutation_page',
78 child: Text('M',
79 style: TextStyle(
80 color: Colors.white,
81 ),
82 ),
83 onPressed: (){
84 Navigator.pushReplacement(context, MaterialPageRoute(
85 builder: ((context){
86 return MutationPage();
87 })
88 ));
89 },
90 ),
91 ],
92 ),
93 body: Query(
94 options: QueryOptions(
95 documentNode: gql(query),
96 ),
97 builder: (
98 QueryResult result, {
99 Refetch refetch,
100 FetchMore fetchMore,
101 }) {
102 if (result.data == null) {
103 return Center(
104 child: Text(
105 "Loading...",
106 style: TextStyle(fontSize: 20.0),
107 ));
108 } else {
109 return ListView.builder(
110 itemBuilder: (BuildContext context, int index) {
111 return ListTile(
112 title: Text(result.data["languages"]["edges"][index]["node"]
113 ['name']),
114 trailing: Text(result.data["languages"]["edges"][index]
115 ["node"]['saveFormat']),
116
117 );
118 },
119 itemCount: result.data["languages"]["edges"].length,
120 );
121 }
122 },
123 ),
124 ),
125 );
126 }
127 }2 - 데이터베이스에 데이터 생성/추가하기
진행하면 graphql_configration.dart를 볼 수 있으며, 이미 GraphQLclient을 설정했습니다. 이제 어디서나 사용할 수 있습니다. 다음으로 database_utils.dart 파일을 생성하고 데이터 생성 작업을 수행합니다. DatabaseUtils{} 클래스를 생성하고 우리가 작업할 데이터 매개변수를 받을 생성자를 만듭니다. 여기서는 langName,saveFormat, 그리고 objectId:
1 import 'package:back4appgraphqldemo/graphql_configration.dart';
2 import 'package:graphql_flutter/graphql_flutter.dart';
3
4 class DatabaseUtils{
5 final String langName,saveFormat,objectId;
6 DatabaseUtils({this.langName="",this.saveFormat="",this.objectId=""});
7 }함수 addData()를 생성합니다. 이 함수는 데이터를 생성하고 GraphQLClient를 초기화하는 비동기 함수가 될 것입니다. GraphQLConfigration 클래스를 초기화합니다. 다음 코드를 sendData() 함수에 붙여넣습니다:
1 Future<QueryResult> sendData() async{
2 String addData='''
3 mutation CreateObject(\$input: CreateLanguageFieldsInput){
4 createLanguage(input: {fields: \$input}){
5 language{
6 name,
7 saveFormat
8 }
9 }
10 }
11 ''';
12 final variable ={
13 "input":{
14 "name" : langName,
15 "saveFormat" : saveFormat,
16 }
17 };
18
19 GraphQlConfiguration configuration = GraphQlConfiguration();
20 GraphQLClient client = configuration.clientToQuery();
21
22 }여기에서 우리는 addData 변수를 초기화하고 데이터를 생성하기 위한 쿼리를 전달했으며 변수를 전달할 쿼리 변수를 초기화했습니다. 또한 쿼리를 전달하는 데 도움이 되는 GraphQLClient를 초기화했습니다. 아래와 같은 방식으로 쿼리를 전달할 수 있습니다:
1 QueryResult queryResult = await client.query(
2 QueryOptions(documentNode: gql(addData), variables: variable),
3 );
4 return queryResult;우리는 GraphQLClient 인스턴스를 사용하여 QueryOptions()를 수용하는 쿼리를 작성했습니다. 이는 지난 튜토리얼에서 보았던 것처럼 쿼리를 전송하는 데 도움이 됩니다. 결과는 queryResult. 당신의 database_utils.dart는 다음과 같아야 합니다:
1 import 'package:back4appgraphqldemo/graphql_configration.dart';
2 import 'package:graphql_flutter/graphql_flutter.dart';
3
4 class DatabaseUtils{
5 final String langName,saveFormat,objectId;
6 DatabaseUtils({this.langName="",this.saveFormat="",this.objectId=""});
7 Future<QueryResult> sendData() async{
8 String addData='''
9 mutation CreateObject(\$input: CreateLanguageFieldsInput){
10 createLanguage(input: {fields: \$input}){
11 language{
12 name,
13 saveFormat
14 }
15 }
16 }
17 ''';
18 final variable ={
19 "input":{
20 "name" : langName,
21 "saveFormat" : saveFormat,
22 }
23 };
24
25 GraphQlConfiguration configuration = GraphQlConfiguration();
26 GraphQLClient client = configuration.clientToQuery();
27
28 QueryResult queryResult = await client.query(
29 QueryOptions(documentNode: gql(addData), variables: variable),
30 );
31 return queryResult;
32 }
33 }이제 UI 클래스 mutation_page.dart. 데이터를 전송하는 버튼을 코딩합시다. 이는 sendDataButtonPressed: 매개변수 안에 코딩할 수 있습니다. langName과 saveFormat이 필요하므로 먼저 비어 있지 않은지 확인한 후 DatabaseUtils의 인스턴스를 생성하고 langName과 saveFormat 매개변수를 전달합니다.
1 if(langName.isNotEmpty && saveFormat.isNotEmpty){
2 utils = DatabaseUtils(
3 langName: langName,
4 saveFormat: saveFormat ,
5 );
6 }이후 sendData() 함수를 DatabaseUtils 인스턴스에서 호출합니다.
이제 당신은 Hot Restart 앱을 실행하고 두 개의 텍스트 필드에 각각의 데이터를 입력한 후 데이터 전송 버튼을 누를 수 있습니다. 이제 플로팅 액션 버튼을 눌러 쿼리 페이지로 돌아가면 테이블에 하나의 데이터가 더 추가된 것을 볼 수 있습니다. 이것이 당신의 MutationPage 클래스의 모습입니다:
1 import 'package:back4appgraphqldemo/app_ui.dart';
2 import 'package:back4appgraphqldemo/database_utils.dart';
3 import 'package:flutter/material.dart';
4
5 class MutationPage extends StatelessWidget {
6
7 String langName,saveFormat,objectId;
8 DatabaseUtils utils;
9
10 @override
11 Widget build(BuildContext context) {
12 return AppUI(
13 onChangedName: (text){
14 langName=text;
15 },
16 onChangedSaveFormat: (text){
17 saveFormat=text;
18 },
19 onChangedObjectId: (text){
20 objectId=text;
21 },
22 sendDataButtonPressed: (){
23 if(langName.isNotEmpty && saveFormat.isNotEmpty){
24 utils = DatabaseUtils(
25 langName: langName,
26 saveFormat: saveFormat ,
27 );
28 utils.sendData();
29 }
30 },
31 deleteDataButtonPressed: (){
32 },
33 updateButtonPressed: (){
34 },
35 );
36 }
37 }
3 - 데이터 업데이트
에서 DatabaseUtils 함수를 생성합니다 Future updateData(). String update를 초기화하고 업데이트 쿼리와 쿼리 변수를 final variables:
1 Future<QueryResult> updateData() async{
2 String update='''
3 mutation UpdateObject(\$id: ID!,\$input: UpdateLanguageFieldsInput){
4 updateLanguage(input: {id:\$id, fields:\$input}){
5 language{
6 name,
7 id
8 }
9 }
10 }
11 ''';
12 final variable={
13 "id":objectId,
14 "input":{
15 "name" : langName
16 }
17 };
18 }이제 우리의 GraphQLClient를 초기화하고 쿼리를 QueryOptions()를 통해 전송합니다. 이렇게 하면 코드가 다음과 같이 보입니다:
1 Future<QueryResult> updateData() async{
2
3 String update='''
4 mutation UpdateObject(\$id: ID!,\$input: UpdateLanguageFieldsInput){
5 updateLanguage(input: {id:\$id, fields:\$input}){
6 language{
7 name,
8 id
9 }
10 }
11 }
12 ''';
13
14 final variable={
15 "id":objectId,
16 "input":{
17 "name" : langName
18 }
19 };
20
21 GraphQlConfiguration configuration = GraphQlConfiguration();
22 GraphQLClient client = configuration.clientToQuery();
23
24 QueryResult queryResult = await client.query(
25 QueryOptions(documentNode: gql(update), variables: variable),
26 );
27 return queryResult;
28 }이제 mutaion_page.dart로 돌아가서 updateButtonPressed: 매개변수에 코드를 작성하세요. langName, objectId 및 saveFormat이 비어 있지 않은지 확인한 후 updateData() 함수를 DatabaseUtils 클래스에서 호출하세요.
1 updateButtonPressed: (){
2 if(langName.isNotEmpty && saveFormat.isNotEmpty && objectId.isNotEmpty){
3 utils = DatabaseUtils(
4 langName: langName,
5 saveFormat: saveFormat ,
6 objectId: objectId
7 );
8 utils.updateData();
9 }
10 },Back4App 대시보드로 가서 업데이트할 언어를 선택한 다음 해당 objectID를 복사하세요. 핫 리스타트를 통해 앱을 다시 시작하고 3개의 텍스트 필드를 모두 채우세요: 첫 번째 텍스트 필드에 삽입할 언어의 새로운 이름과 두 번째 필드에 새로운 저장 형식 및 세 번째 필드에 objectId를 입력하세요. 이제 데이터 업데이트 버튼을 눌러 앱에서 업데이트된 정보를 확인하세요. 오른쪽 하단의 Q 플로팅 액션 버튼을 클릭하세요.

4 - 데이터 삭제
다음과 같이 deleteData() 비동기 함수를 DatabaseUtils에서 생성하고 String delete를 초기화한 후, 데이터를 삭제하기 위한 GraphQL 쿼리를 전달합니다. 최종 변수를 가져와 쿼리 변수를 전달합니다.
1 Future<QueryResult> deleteData() async{
2 String delete='''
3 mutation DeleteObject(\$id: ID!){
4 deleteLanguage(input: {id:\$id}){
5 language{
6 name,
7 id
8 }
9 }
10 }
11 ''';
12 final variable={
13 "id":objectId,
14 };
15 }여기서 우리는 삭제해야 할 행의 objectId만 필요합니다. GraphQLClient를 초기화하고 QueryOptions()를 통해 쿼리를 전송합니다.
1 Future<QueryResult> deleteData() async{
2 String delete='''
3 mutation DeleteObject(\$id: ID!){
4 deleteLanguage(input: {id:\$id}){
5 language{
6 name,
7 id
8 }
9 }
10 }
11 ''';
12 final variable={
13 "id":objectId,
14 };
15
16 GraphQlConfiguration configuration = GraphQlConfiguration();
17 GraphQLClient client = configuration.clientToQuery();
18
19 QueryResult queryResult = await client.query(
20 QueryOptions(documentNode: gql(delete), variables: variable),
21 );
22
23 return queryResult;
24 }에서 MutationPage 의 deleteDataButtonPressed: 매개변수는 objectId가 비어 있거나 null이 아닌지 확인하고 deleteData() 함수를 호출합니다. 핫 리스타트 애플리케이션에서 삭제할 행의 objectId를 입력하고 데이터 삭제 버튼을 누릅니다. 특정 행이 Language 클래스에서 삭제되어야 합니다.

성공적으로 앱이 마침내 변환 작업을 완료했습니다 !!!
main.dart 는 다음과 같아야 합니다:
1 import 'package:back4appgraphqldemo/mutation_page.dart';
2 import 'package:flutter/material.dart';
3 import 'package:graphql_flutter/graphql_flutter.dart';
4 import 'consonents.dart';
5 import 'dart:ui';
6
7 void main() {
8 runApp(MyApp());
9 }
10
11 class MyApp extends StatelessWidget {
12 @override
13 Widget build(BuildContext context) {
14 final HttpLink httpLink = HttpLink(
15 uri: 'https://parseapi.back4app.com/graphql',
16 headers: {
17 'X-Parse-Application-Id': kParseApplicationId,
18 'X-Parse-Client-Key': kParseClientKey,
19 'X-Parse-Master-Key': kParseMasterKey,
20 //'X-Parse-REST-API-Key' : kParseRestApiKey,
21 }, //getheaders()
22 );
23
24 ValueNotifier<GraphQLClient> client = ValueNotifier(
25 GraphQLClient(
26 cache: OptimisticCache(dataIdFromObject: typenameDataIdFromObject),
27 link: httpLink,
28 ),
29 );
30
31 return MaterialApp(
32 home: GraphQLProvider(
33 child: MyHomePage(),
34 client: client,
35 ),
36 );
37 }
38 }
39
40 class MyHomePage extends StatefulWidget {
41 @override
42 _MyHomePageState createState() => _MyHomePageState();
43 }
44
45 class _MyHomePageState extends State<MyHomePage> {
46 String name;
47 String saveFormat;
48 String objectId;
49
50 String query = '''
51 query FindLanguages{
52 languages{
53 count,
54 edges{
55 node{
56 name,
57 saveFormat
58 }
59 }
60 }
61 }
62 ''';
63
64 @override
65 Widget build(BuildContext context) {
66 return SafeArea(
67 child: Scaffold(
68 appBar: AppBar(
69 title: Text(
70 'Parsing data using GraphQL',
71 ),
72 ),
73 floatingActionButton: Row(
74 mainAxisAlignment: MainAxisAlignment.end,
75 children: [
76 FloatingActionButton(
77 heroTag: 'mutation_page',
78 child: Text('M',
79 style: TextStyle(
80 color: Colors.white,
81 ),
82 ),
83 onPressed: (){
84 Navigator.pushReplacement(context, MaterialPageRoute(
85 builder: ((context){
86 return MutationPage();
87 })
88 ));
89 },
90 ),
91 ],
92 ),
93 body: Query(
94 options: QueryOptions(
95 documentNode: gql(query),
96 ),
97 builder: (
98 QueryResult result, {
99 Refetch refetch,
100 FetchMore fetchMore,
101 }) {
102 if (result.data == null) {
103 return Center(
104 child: Text(
105 "Loading...",
106 style: TextStyle(fontSize: 20.0),
107 ));
108 } else {
109 return ListView.builder(
110 itemBuilder: (BuildContext context, int index) {
111 return ListTile(
112 title: Text(result.data["languages"]["edges"][index]["node"]
113 ['name']),
114 trailing: Text(result.data["languages"]["edges"][index]
115 ["node"]['saveFormat']),
116
117 );
118 },
119 itemCount: result.data["languages"]["edges"].length,
120 );
121 }
122 },
123 ),
124 ),
125 );
126 }
127 }database_utils.dart 는 다음과 같아야 합니다:
1 import 'package:back4appgraphqldemo/graphql_configration.dart';
2 import 'package:graphql_flutter/graphql_flutter.dart';
3
4 class DatabaseUtils{
5
6 final String langName,saveFormat,objectId;
7
8 DatabaseUtils({this.langName="",this.saveFormat="",this.objectId=""});
9
10 String delete='''
11 mutation DELETE_LANGUAGES(\$id: ID!){
12 deleteLanguage(input: {id:\$id}){
13 language{
14 name,
15 id
16 }
17 }
18 }
19 ''';
20
21 String addData='''
22 mutation CREATE_LANGUAGES(\$input: CreateLanguageFieldsInput){
23 createLanguage(input: {fields: \$input}){
24 language{
25 name,
26 saveFormat
27 }
28 }
29 }
30 ''';
31 String update='''
32 mutation UPDATE_LANGUAGES(\$id: ID!,\$input: UpdateLanguageFieldsInput){
33 updateLanguage(input: {id:\$id, fields:\$input}){
34 language{
35 name,
36 id
37 }
38 }
39 }
40 ''';
41
42 Future<QueryResult> sendData() async{
43
44 final variable ={
45 "input":{
46 "name" : langName,
47 "saveFormat" : saveFormat,
48 }
49 };
50 print('sendingData');
51
52 GraphQlConfiguration configuration = GraphQlConfiguration();
53 GraphQLClient client = configuration.clientToQuery();
54
55 QueryResult queryResult = await client.query(
56 QueryOptions(documentNode: gql(addData), variables: variable),
57 );
58 return queryResult;
59
60 }
61 Future<QueryResult> updateData() async{
62 final variable={
63 "id":objectId,
64 "input":{
65 "name" : langName
66 }
67 };
68
69 GraphQlConfiguration configuration = GraphQlConfiguration();
70 GraphQLClient client = configuration.clientToQuery();
71
72 QueryResult queryResult = await client.query(
73 QueryOptions(documentNode: gql(update), variables: variable),
74 );
75 return queryResult;
76 }
77
78
79 Future<QueryResult> deleteData() async{
80 final variable={
81 "id":objectId,
82 };
83
84 GraphQlConfiguration configuration = GraphQlConfiguration();
85 GraphQLClient client = configuration.clientToQuery();
86
87 QueryResult queryResult = await client.query(
88 QueryOptions(documentNode: gql(delete), variables: variable),
89 );
90
91 return queryResult;
92 }
93 }mutaion_page.dart는 다음과 같아야 합니다 :
1 import 'package:back4appgraphqldemo/app_ui.dart';
2 import 'package:back4appgraphqldemo/database_utils.dart';
3 import 'package:flutter/material.dart';
4
5 class MutationPage extends StatelessWidget {
6
7 String langName,saveFormat,objectId;
8 DatabaseUtils utils;
9
10 @override
11 Widget build(BuildContext context) {
12 return AppUI(
13 onChangedName: (text){
14 langName=text;
15 },
16 onChangedSaveFormat: (text){
17 saveFormat=text;
18 },
19 onChangedObjectId: (text){
20 objectId=text;
21 },
22
23 sendDataButtonPressed: (){
24 if(langName.isNotEmpty && saveFormat.isNotEmpty){
25 utils = DatabaseUtils(
26 langName: langName,
27 saveFormat: saveFormat ,
28 );
29 utils.sendData();
30 }
31 },
32 deleteDataButtonPressed: (){
33 if(objectId.isNotEmpty){
34 utils = DatabaseUtils(
35 objectId: objectId,
36 );
37 utils.deleteData();
38 }
39 },
40 updateButtonPressed: (){
41 if(langName.isNotEmpty && saveFormat.isNotEmpty && objectId.isNotEmpty){
42 utils = DatabaseUtils(
43 langName: langName,
44 saveFormat: saveFormat ,
45 objectId: objectId
46 );
47 utils.updateData();
48 }
49 },
50 );
51 }
52 }결론
이 가이드에서는 Flutter 앱에서 GraphQL 변이를 사용하여:
- Back4App에서 새 객체를 생성합니다;
- Back4App에서 객체를 업데이트합니다;
- Back4App에서 객체를 삭제합니다.
이 시점에서 여러분은 완전한 기능을 갖춘 Flutter GraphQL CRUD 프로젝트를 가지고 있으며, 이를 시작점으로 삼아 다음 앱을 개발할 수 있습니다. 다음 가이드에서는 쿼리에 대해 깊이 파고들어 Back4App에서 데이터를 검색하고 Flutter 앱에 표시하는 방법을 보여줄 것입니다.
좋은 하루 되세요!