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
<!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 앱에 포함시킨다
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
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를 추가해주는 알림을 눌러 설치해준다
참고)
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
* 해결 방법 *
const를 하나하나 다 넣어줘야하나 const인지 판별하다 오히려 오류가 날 수도 있다는 생각에
VSCode의 analysis_optional.yaml을 수정해주기로 했다
rules : 부분에 해당 코드 3줄을 입력해준다 (들여쓰기를 맞춰서 해줘야함!)
prefer_const_constructors: false
prefer_const_literals_to_create_immutables: false
prefer_const_constructors_in_immutables: false
=> problems에 뜨던 에러와 파란줄이 모두 사라졌다
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.
구글링해서 HTML Guide를 보니 "autocomplete" attribute에 관한 내용이 나왔다
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를 이용해 버퍼링 설정을 추가하거나 메시지 버퍼링을 처리하도록 설정해주면 된다
//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 생성
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>
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
2. Flutter 개발환경 설정_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발
https://tiny-immj.tistory.com/74
3. 기초 프로젝트 생성, AVD(안드로이드) 실행_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발
https://tiny-immj.tistory.com/75
4. Hello World 코드 분석 _Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발
https://tiny-immj.tistory.com/76
5. Dart 언어 기초_ Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발
https://tiny-immj.tistory.com/77
6. Bootstrap으로 UI 구성하기_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발
https://tiny-immj.tistory.com/83
'프로젝트 기록 > Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발' 카테고리의 다른 글
flutter 웹 개발 index.html과 main.dart, 실행 순서에 대한 이해 (2) | 2024.11.15 |
---|---|
Hello World 출력하는 웹사이트 만들기, Entrypoint, 위젯트리_Flutter와 Bootstrap을 활용한 크로스 플랫폼 웹 개발 (3) | 2024.11.13 |
6. Bootstrap으로 UI 구성하기_Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발 (4) | 2024.10.21 |
5.Dart 언어 기초1_Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발 (2) | 2024.09.11 |
4.Hello World 코드 분석_Flutter, Bootstrap을 활용한 크로스 플랫폼 웹 개발 (1) | 2024.09.11 |