본문 바로가기
프로젝트 기록/Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발

7. Bootstrap 폼 디자인, flutter dart 에러 해결 _Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발

by jeong11 2024. 11. 8.
반응형

Bootstrap을 사용한 폼 디자인

1. Bootstrap 폼

Bootstrap은 웹에서 폼을 디자인할 때 유용한 CSS 클래스를 제공한다
간결한 반응형 폼을 쉽게 만들 수 있고 다양한 입력 필드, 버튼, 레이아웃 옵션 등을 제공함 

 

Bootstrap 구성요소 

▷ form-group : 폼 필드를 그룹화하는 클래스

▷ form-control : 텍스트 입력 필드, 선택 상자 등의 스타일링을 위한 클래스 

▷ form-check : 체크박스와 라디오 버튼을 위한 클래스

▷ btn : 버튼 스타일링을 위한 클래스

 

2. Flutter와의 연동방법 

Bootstrap을 활용해 폼을 디자인한 후 사용자 입력을 처리할 수 있다 

Bootstrap 폼을 HtmlElementView를 통해 Flutter UI에 삽입하고 JavaScript나 Flutter 내에서 입력 데이터를 처리하는 방식으로 연동할 수 있다 

 

 

3. Bootstrap을 사용한 반응형 폼 디자인

3-1. 프로젝트 설정

> web/index.html 

<head> 태그 내 Bootstrap을 추가한다 

<!-- Bootstrap CSS -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">

 

 

3-2. Bootstrap 폼 코드

> web/index.html

index.html

<!DOCTYPE html>
<html>
<head>
<!--제일 먼저 index 파일을 찾아 렌더링-->
  <base href="$FLUTTER_BASE_HREF">

  <!-- Bootstrap CSS -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
  
  <!-- Bootstrap JS and dependencies -->
  <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="hello_world">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="icon" type="image/png" href="favicon.png"/>

  <title>hello_world</title>
  <link rel="manifest" href="manifest.json">
</head>
<body>
  <script src="flutter_bootstrap.js" async></script>

  <!--12강-->
  <div class="container mt-5">
    <h2>Contact Us</h2>
    <form id="contact-form">
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name" placeholder="Enter your name">
      </div>
      <div class="form-group">
        <label for="email">Email address</label>
        <input type="email" class="form-control" id="email" placeholder="Enter your email">
      </div>
      <div class="form-group">
        <label for="message">Message</label>
        <textarea class="form-control" id="message" rows="3" placeholder="Enter your message">
        </textarea>
      </div>
      <button type="submit" class="btn btn-primary">
        Submit
      </button>
    </form>
  </div>
</body>
</html>

 

 

3-3. 코드 설명

<div class="container mt-5">

container : 폼이 포함된 컨테이너로 폼을 중앙에 정렬하고 여백을 추가한다

<div class="form-group">

form-group : 각 입력 필드를 그룹화하여 폼의 레이아웃을 구성한다

<input type="email" class="form-control" id="email" placeholder="enter your email">

form-control : 입력 필드와 텍스트 영역을 스타일링하기 위해 사용된다

<button type="submit" class="btn btn-primary">

btn : 제출 버튼을 스타일링한다 

btn-primary 클래스를 사용하여 Bootstrap의 기본 파란색 버튼을 적용

 

 

4. Bootstrap 폼을 활용한 사용자 입력 처리

4-1. Flutter와 Bootstrap 폼 연동

> lib/main.dart 

HtmlElementView를 사용해 Bootstrap 폼을 Flutter 앱에 포함시킨다

main.dart

import 'package:flutter/material.dart';
import 'dart:html' as html;

void main() {
  //runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter & Bootstrap Form'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'Please fill out the form below:',
                style: TextStyle(fontSize: 20),
              ),
              const SizedBox(height: 20),
              HtmlElementView(viewType: 'contact-form'),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: _handleFormSubmission,
                child: const Text('Submit Form'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  static void _handleFormSubmission() {
    // 여기에서 JavaScript를 사용하여 폼 데이터를 처리할 수 있습니다.
    html.window.alert('Form Submitted!');
  }
}

 

 

4-2. 코드 설명

HtmlElementView(viewType: 'contact-form'),

HtmlElementView : Bootstrap 폼을 Flutter 위젯으로 포함시킨다

viewType으로 HTML 요소의 ID를 지정하여 Flutter UI에 삽입한다

 

static void _handleFormSubmission() {
	//여기에서 JavaScript를 사용하여 폼 데이터를 처리할 수 있습니다
    html.window.alert('Form Submitted!');
}

_handleFormSubmission 함수 : 제출 버튼을 클릭했을 때 호출된다

JavaScript를 사용해 폼 데이터를 처리하거나 간단히 알림 창을 띄울 수 있다 

 

 

5. 실행화면 

VsCode 내 터미널에서 실행 

flutter run -d chrome

 

chrome 실행 화면

 

 

6. 에러해결 - 코드 수정

6-1. main.dart 수정

1) import 'dart:html' as html; 관련 에러

import 'dart:html' as html;
Don't use web-only libraries outside Flutter web plugin packages, dart(avoid_web_libraries_in_flutter) [Ln3, Col 1]
Try finding a different library for you needs. 

 

dart:html을 가져오는 과정에서 오류가 발생했다

플러터 패키지 외부의 웹 전용 라이브러리 사용을 피하세요 
이것은 실제로 Android, ios 앱을 빌드하는 것을 방지합니다

 

*해결 방법*

universal_html 패키지 사용 

: universal_html 패키지는 브라우저, Dart VM, Flutter를 지원하며 dart:html 기타 웹 라이브러리의 대체품이다 

import 'package:universal_html/html.dart' as html;

 

=> 빨간줄이 그어지며 해당 패키지를 찾을 수가 없다고 나오면 오른쪽 밑 dependency를 추가해주는 알림을 눌러 설치해준다 

 

프로젝트에서 universal_html 검색하면 설치된 것을 확인할 수 있음

 

참고)

https://stackoverflow.com/questions/60646793/avoid-using-web-only-libraries-outside-flutter-web-plugin-packages

 

Avoid using web-only libraries outside Flutter web plugin packages

I'm building a Flutter app that I am trying to make work on the web. Part of it contains some web specific code: import 'dart:html' as html; import 'package:flutter/foundation.dart'; class

stackoverflow.com

 

 

2) use 'const' with the constructor to improve perfomance. dart(prefer_const_constructors) 에러

use 'const' with the constructor to improve perfomance. dart(prefer_const_constructors)
Try adding the 'const' keyword to the constructor invocation 

 

4개나 등장

 

* 해결 방법 *

const를 하나하나 다 넣어줘야하나 const인지 판별하다 오히려 오류가 날 수도 있다는 생각에 

VSCode의 analysis_optional.yaml을 수정해주기로 했다 

analysis_optional.yaml의 rules

rules : 부분에 해당 코드 3줄을 입력해준다 (들여쓰기를 맞춰서 해줘야함!)

    prefer_const_constructors: false
    prefer_const_literals_to_create_immutables: false
    prefer_const_constructors_in_immutables: false

 

=> problems에 뜨던 에러와 파란줄이 모두 사라졌다

gone

 

 

3) An element doesn't have an autocomplete attribute 이슈

> web/index.html 수정

A form field has an id or name attribute that the browser's autofill recognizes. However, it doesn't have an autocomplete attribute assigned. This might prevent the browser from correctly autofilling the form.
To fix this issue, provide an autocomplete attribute.

이슈 - 2 resources
Name과 Email input 태그에 이슈 표시

구글링해서 HTML Guide를 보니 "autocomplete" attribute에 관한 내용이 나왔다 

문제가 되는 input 태그

type이 hidden이 아닐땐 autocomplete="on" 이나 autocomplete="off"가 있어야한다는 내용같았다

 

 

* 해결 방법 *

<input type="text" class="form-control" id="name" placeholder="Enter your name" autocomplete="on">
<input type="email" class="form-control" id="email" placeholder="Enter your email" autocomplete="on">

autocomplete="on"을 추가해줬다

완료

 

 

6-2. flutter run -d chrome 실행 시 나타나는 로그 이슈

A message on the flutter/lifecycle channel was discarded before it could handled.
This happens when pluggin sends messages to the framework side before the framework has had an opportunity to register a listener. See the ChannelBuffers API documentation for details on how to configure the channel to expect more messages, or to expect messages to get discarded: 

나오는 에러 로그

주로 플러그인이 Flutter 프레임워크에서 리스너가 준비되기 전에 메시지를 보내기 때문에 발생한다 

 

* 해결방법 *

1. head 태그의 script 추가 > 해결하지 못함

2. Flutter 버전을 안정된 버전으로 설정해보기 > 이 문제는 해결하지 못함

> flutter 웹은 최신버전에서 호환성 문제를 일으킬 수 있으니 stable 채널로 변경하고 다시 설치해봤지만 안됨 

flutter channel stable
flutter upgrade
flutter clean
flutter pub get
flutter build web

 

3. 챗gpt한테 물어봄 

1) 에러 메시지에 언급된 ChannelButters API를 이용해 버퍼링 설정을 추가하거나 메시지 버퍼링을 처리하도록 설정해주면 된다

main.dart 코드 수정

//runApp(const MyApp()); 주석을 제거해주고 

메시지 버퍼링을 처리하는 부분을 넣어줬다 

Flutter에서 비동기 처리 방식은 Future와 async / await를 사용해 네이티브 코드와의 상호작용을 비동기적으로 처리한다

async으로 정의된 함수는 메세지를 보내고, 결과를 기다리지 않고 즉시 다음 작업을 수행할 수 있도록 해준다

 

> main.dart 전체 코드

//main.dart
//2024-10-21 Text, Image 위젯 사용
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:universal_html/html.dart' as html;
import 'package:logger/logger.dart';

final logger = Logger();

void main() {
  
  WidgetsFlutterBinding.ensureInitialized();

  // 네이티브와 통신하기 위한 MethodChannel 초기화
  final platform = MethodChannel('com.example/your_channel_name');

  // WidgetsBinding을 사용하여 Flutter 초기화가 완료된 후 작업을 진행
  WidgetsBinding.instance.addPostFrameCallback((_) {
    // Flutter가 초기화된 후 네이티브에 메시지 전송
    sendMessageToNative(platform);
  });

   runApp(const MyApp());
}

// sendMessageToNative 함수 정의
Future<void> sendMessageToNative(MethodChannel platform) async {
  try {
    final String result = await platform
        .invokeMethod('sendMessage', {'message': 'Hello from Flutter!'});
    logger.d('Message from Native: $result'); // d는 debug 레벨 로그
  } on PlatformException catch (e) {
    logger
        .e('Failed to send message to native: ${e.message}'); // e는 error 레벨 로그
  }
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  //엔트리포인트 main이 먼저 실행됨 - run APP, -runApp(const MyApp());
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter & Bootstrap Form'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'Please fill out the form below:',
                style: TextStyle(fontSize: 20),
              ),
              const SizedBox(height: 20),
              HtmlElementView(viewType: 'contact-form'),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: _handleFormSubmission,
                child: const Text('Submit Form'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  static void _handleFormSubmission() {
    // JavaScript를 사용하여 폼 데이터를 처리할 수 있습니다.
    html.window.alert('Form Submitted!');
  }
}

 

sendMessageToNative 함수는 flutter와 네이티브(Android/ios)간의 메시지를 주고 받기 위해 정의해야 하는 함수 

이 함수는 Flutter에서 네이티브 코드로 메시지를 보내거나 네이티브 코드에서 Flutter로 결과를 반환하는 역할을 한다 

Flutter에서 네이티브와 통신하기 위해서 MethodChannel를 사용해 메시지를 주고 받는다

 

2) 메시지 버퍼링 설정 진행 중 

> MainActivity.java 생성

MainActivity.java

import android.os.Bundle;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "com.example/your_channel_name";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        new MethodChannel(getFlutterEngine().getDartExecutor(), CHANNEL)
            .setMethodCallHandler(
              (call, result) -> {
                if (call.method.equals("sendMessage")) {
                    String message = call.argument("message");
                    System.out.println("Message from Flutter: " + message);
                    result.success("Message received in Native!");
                } else {
                    result.notImplemented();
                }
              }
            );
    }
}

네이티브가 MethodChannel을 통해 Flutter로부터 받은 메시지를 처리한다 

sendMessage 메소드가 호출되면 Flutter에서 전달한 메시지를 콘솔에 출력하고 네이티브에서 메시지를 처리한 후 Flutter로 결과를 반환한다 

 

3) 코드 설명 

MethodChannel 설정 : 

▷Flutter와 네이티브(Android/ios)간의 메시지 통신을 위해 MethodChannel을 사용한다 

▷MethodChannel은 Flutter에서 네이티브로 메시지를 보내거나 네이티브에서 Flutter로 메시지를 보낼 때 사용한다 

 

SendMessageToNative 함수 : 

▷SendMessageToNative 함수는 MethodChannel을 사용해 네이티브 코드로 메시지를 전송하는 역할을 한다

platform.invokeMethod('sendMessage', {'message' : 'Hello from Flutter!'})는 sendMessage라는 메소드를 네이티브에서 호출하도록 요청하고, 추가적인 데이터를 전달한다 

▷네이티브가 이 메시지를 받으면 result.success("Message received in Native!")와 같이 결과를 Flutter에 반환한다

 

네이티브(Android/ios)측 코드 : 

▷네이티브에서 MethodChannel을 통해 Flutter로부터 받은 메시지를 처리한다

▷sendMessage 메소드가 호출되면 Flutter에서 전달한 메시지를 콘솔에 출력하고, 네이티브에서 메시지를 처리한 후 Flutter로 결과를 반환한다

 

 

7. 폼 제출 시 알람 

▷폼 제출 및 데이터 처리

폼에 데이터를 입력하고 "Submit Form" 버튼을 클릭해 제출 과정을 테스트한다 

제출 후 alert를 통해 폼이 성공적으로 제출되었음을 알 수 있다

 

* 이슈 상황 *

submit 버튼을 누르면 alert가 안 뜨고 아무일도 일어나지 않았다
코드나 콘솔 내 에러가 없어서 어떤 부분이 잘못되었는지 알 수가 없었다 

 

* 원인 *

 

main.dart

ElevateButton(
	onPressed: _handleFormSubmission,
    child : const Text('Submit Form')
),
static void _handleFormSubmission() {
	html.window.alert('Form Submitted!');
}

 

index.html

<button type="submit" class="btn btn-primary">
	submit
</button>

 

▷ ElevateButton의 _handleFormSubmission 함수가 index.html의 submit 버튼 이벤트를 제어할 수 없다

▷ index.html의 submit 버튼은 html 내에서 동작하는 요소이고 ElevateButton은 Flutter의 버튼이기 때문에 서로 독립적으로 동작된다

 

당연히 연결이 되어있는줄 알았는데 아니었다!! 

 

 

* 문제 해결 *

▷ index.html에서 submit 버튼 클릭 이벤트를 따로 만들어주기

<form id="contact-form">

JavaScript가 contact-form 엘리먼트를 찾을 수 있도록, 위치는 <body>태그의 맨 마지막에 추가해준다

 

index.html submit 버튼 이벤트 만들기

<script>
	document.getElementById('contact-form').addEventListener('submit', function (event) {
    	event.preventDefault();		//기본 폼 제출 동작을 방지
        alert("Form Submitted!");	//확인용 알림
    });
</script>

 

 

드디어 alert가 떴다

 

index.html 전체 코드 

<!DOCTYPE html>
<html>

<head>
  <!--제일 먼저 index 파일을 찾아 렌더링-->
  <base href="$FLUTTER_BASE_HREF">

  <!-- Bootstrap CSS -->
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">

  <!-- Bootstrap JS and dependencies -->
  <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.2/dist/umd/popper.min.js"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="hello_world">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="icon" type="image/png" href="favicon.png" />

  <title>hello_world</title>
  <link rel="manifest" href="manifest.json">
</head>

<body>
  <script src="flutter.js" async></script>
  <!-- <script src="flutter_bootstrap.js" async></script> -->

  <!--12강-->
  <div class="container mt-5">
    <h2>Contact Us</h2>
    <form id="contact-form">
      <div class="form-group">
        <label for="name">Name</label>
        <input type="text" class="form-control" id="name" placeholder="Enter your name">
      </div>
      <div class="form-group">
        <label for="email">Email address</label>
        <input type="email" class="form-control" id="email" placeholder="Enter your email">
      </div>
      <div class="form-group">
        <label for="message">Message</label>
        <textarea class="form-control" id="message" rows="3" placeholder="Enter your message">
        </textarea>
      </div>
      <button type="submit" class="btn btn-primary">
        Submit
      </button>
    </form>
  </div>

  <script>
    document.getElementById('contact-form').addEventListener('submit', function (event) {
      event.preventDefault();  // 기본 폼 제출 동작을 방지
      alert("Form Submitted!");  // 확인용 알림
    });
  </script>

</body>
</html>

 

 

 


시리즈

 

1. 설정_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발

https://tiny-immj.tistory.com/73

 

1. 설정_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발

'Docker 설치 및 기본 실습'과 'Flutter 환경 설정'으로 진행된다 Docker 설치 및 기본 실습 1. Docker 설치(for window)> Docker 공식 웹사이트에서 Docker Desktop for Windows 설치파일을 다운로드하기 https://docs.doc

tiny-immj.tistory.com

 

 

 

 

 

2. Flutter 개발환경 설정_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발

https://tiny-immj.tistory.com/74

 

2.Flutter 개발 환경 설정_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발

개발환경 선택플러터 개발을 할 때는 대상 웹과 동일한 플랫폼에서 해야 합니다즉 Mac OS용으로 개발하기 위해선 Mac이 필요하고 Windows 타깃으로 개발을 하기 위해선 Windows PC가 필요합니다 저는

tiny-immj.tistory.com

 

 

 

 

 

3. 기초 프로젝트 생성, AVD(안드로이드) 실행_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발

https://tiny-immj.tistory.com/75

 

3. 기초 프로젝트 생성_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발

Flutter 프로젝트 실습 : Hello World 프로젝트 만들기 cmd에서 Flutter 프로젝트를 생성해보자 $ flutter create [프로젝트명]$ flutter create hello_world ※ 프로젝트명은 소문자로만 작성이 가능하다 숫자가

tiny-immj.tistory.com

 

 

 

4. Hello World 코드 분석 _Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발

https://tiny-immj.tistory.com/76

 

4.Hello World 코드 분석_Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발

Hello World 프로젝트 분석하기1. 해당 코드https://tiny-immj.tistory.com/75 Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발_3. 기초 프로젝트 생성Flutter 프로젝트 실습 : Hello World 프로젝트 만들기 cmd에서

tiny-immj.tistory.com

 

 

5. Dart 언어 기초_ Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발

https://tiny-immj.tistory.com/77

 

5.Dart 언어 기초1_Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발

Dart 언어의 기초 1. 변수와 데이터 타입1) Dart의 역할Dart : Flutter의 기본 프로그래밍 언어, 강력한 타입 시스템과 간결한 문법이 특징 Flutter에서 Dart의 역할 : Dart는 Flutter 애플리케이션의 모든 로

tiny-immj.tistory.com

 

6. Bootstrap으로 UI 구성하기_Flutter와  Bootstrap을 활용한 크로스 플랫폼 웹 개발

https://tiny-immj.tistory.com/83

 

6. Bootstrap으로 UI 구성하기_Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발

Bootstrap으로 UI 기본 구성하기1. Bootstrap과 Flutter Bootstrap : 프론트엔드 프레임워크 중 하나로 반응형 웹 디자인을 쉽게 구현할 수 있는 다양한 CSS, JavaScript 컴포넌트를 제공한다. 기본적으로 미리

tiny-immj.tistory.com

 

반응형