Study/Dart,Flutter

13. Flutter[플러터] 안드로이드 화면 띄우기 methodchannel invokeMethod(2)

코딩 잘 할거얌:) 2022. 6. 25. 00:43
반응형

이전에 포스팅한 것에 이어서 이번에는 플러터에서 안드로이드 화면을 띄워보도록 하겠다.

안드로이드 : 실례합니다..

이전 포스팅에서도 마찬가지로 플러터화면 위에 안드로이드 화면을 띄우는게 필요한 경우가 있다. 나 역시 회사 프로젝트 진행 중 안드로이드의 aar파일을 띄워야 하는 경우가 있어서 사용했었다.


이전 포스팅 요약

 

이전 포스팅을 요약해서 설명하자면,

 

메소드체널을 이용하여 화면을 띄울 것이다. 간단하다. 플러터에서 invokemethod를 호출하면 startActivity를 실행시킬것이다.

 


파일 구성 및 버전정보

 

안드로이드는 Java로 진행하겠다.

 

android

app.src.main.java.com.example.flutter_android
MainActivity.java
MainActivity2.java

...

lib

main.dart


Flutter 3.0.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 676cefaaff (2 days ago) • 2022-06-22 11:34:49 -0700
Engine • revision ffe7b86a1e
Tools • Dart 2.17.5 • DevTools 2.12.2

 


Flutter

 

쓸데없는 코드는 최소화하기 위해 아주 담백하게 기본 코드에서 진행하도록 하겠다.

 

 

우선 플러터는 생성할 때 Java로 진행하니 Java로 생성하자. 물론 cmd로 파일을 생성할 때

 

flutter create -a java 프로젝트 이름

이렇게 작성하면 안드로이드 파일이 java로 생성이 되지만, 

 

flutter create -a java .  (뒤의. 은 현재 폴더에 적용한다는 뜻)

 

을 해도 현재폴더에 있는 flutter프로젝트에 java로 된 안드로이드 파일이 생성이 된다. 단, Kotlin과 같이 있는 상태에서 컴파일하면 오류가 뜰 확률이 상당히 높으므로 Kotlin으로 된 것은 지우는 게 좋다.

 

https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects

 

GitHub - flutter/flutter: Flutter makes it easy and fast to build beautiful apps for mobile and beyond

Flutter makes it easy and fast to build beautiful apps for mobile and beyond - GitHub - flutter/flutter: Flutter makes it easy and fast to build beautiful apps for mobile and beyond

github.com

위의 링크를 보고 따라 하면 쉽게 따라 할 수 있지만, 하나하나 설명도 하며 진행을 하겠다.

 

 

 final platform = const MethodChannel("testing.flutter.android");

우선 메서드 채널을 연결하기 위해서는 플러터와 안드로이드에 서로 같은 문자열을 적고 약속을 해야 한다. 그래서 MethodChannel에 들어갈 매개변수와 안드로이드에 MethodChannel에 들어갈 문자열은 같아야 한다.

 

  Future<void> _showActivity() async {
    try {
      await platform.invokeMethod('showActivity');
    } on PlatformException catch (e) {
      log("ERROR  : $e");
    }
  }
  1. 플러터 프로젝트를 생성하면 존재하는 FloatingActionButton의 onTap에 적용해 줄 함수이다. 메서드 채널은 비동기로 진행이 된다.
  2. try on catch문을 활용해서 비동기에 발생하는 오류를 잡는다.
  3. await 해서 비동기가 끝날 때까지 기다린다. 그리고 invokeMethod안에 들어가는 매개변수 'showActivity'는 안드로이드에 showActivity라는 값으로 전달이 되고, 안드로이드에서는 전달받은 값으로 switch case문으로 각각에 맞게 실행을 할 것이다.

나머지는 기본 생성되는 코드와 다른 것이 없다.

 

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  //MethodChannel이름을 선언해준다. 안드로이드에 선언해준 것과 반드시 같아야 한다.
  final platform = const MethodChannel("testing.flutter.android");

  //floating ActionButton을 누르면 실행 될 함수.
  //Android의 activity를 비동기로 띄울 것이다.
  Future<void> _showActivity() async {
    try {
      await platform.invokeMethod('showActivity');
    } on PlatformException catch (e) {
      log("ERROR  : $e");
    }
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(

        title: Text(widget.title),
      ),
      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              'Show Activity',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          await _showActivity();
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

 


 

Android

 

우선 VSCode를 사용하는 경우라면, 안드로이드 폴더를 우측 클릭하여 android studio를 실행한다.

그리고 그림의 좌측 상단에 Android라고 되어있는 것을 클릭해서 프로젝트로 바꿔주고 MainActivity가 있는 파일에 EmptyActivty를 생성해준다.

그러면 Java파일이 하나 생성이 되며 Gradle과 AndroidManifest파일이 업데이트가 된다. (Activity가 추가될 때마다 Manifest파일에 적어줘야 하고, Gradle 버전도 맞춰준다.)

package com.example.flutter_android_java;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity2 extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

생성된 MainActivity2

 

MainActivity를 손봐주자. 우선 위쪽 Flutter Wiki를 참고하면 되지만, 설명을 한번 더 하겠다.

 

private static final String CHANNEL = "testing.flutter.android";

 

이건 플러터에서 한 것과 마찬가지로 채널 이름을 맞춰야 하는 것이므로 반드시 플러터에서 사용한 것과 동일한 상수를 사용해야 한다.

 new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
                .setMethodCallHandler(
                        (call, result) -> {
                            if (call.method.equals("showActivity")) {
                                Intent intent = new Intent(MainActivity.this, MainActivity2.class);
                                startActivity(intent);

                                result.success("");
                            } else {
                                result.error("UNAVAILABLE", "Cannot Start Activity.", null);
                            }

                        }
                );

 

이 부분이 메서드 채널과 연결되는 부분이다. 플러터에서 showActivity가 호출이 되면 if문을 타고 같은 것과 매칭이 되어 실행이 된다. activity를 실행하는 것은 Intent를 선언해주고, startActivity로 선언한 intent를 넣으면 된다.

 

package com.example.flutter_android_java;

import android.content.Intent;

import androidx.annotation.NonNull;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;


public class MainActivity extends FlutterActivity {
    private static final String CHANNEL = "testing.flutter.android";


    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);
        new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
                .setMethodCallHandler(
                        (call, result) -> {
                            if (call.method.equals("showActivity")) {
                                Intent intent = new Intent(MainActivity.this, MainActivity2.class);
                                startActivity(intent);

                                result.success("");
                            } else {
                                result.error("UNAVAILABLE", "Cannot Start Activity.", null);
                            }

                        }
                );
    }


}

여기까지 따라 하면 아마 웬만한 분들은 

import io.flutter.plugins.GeneratedPluginRegistrant;

이 부분이 오류 난다고 뜰 것이다. 걱정하지 말고 컴파일 하자. 어떤 문제인지는 모르겠지만, 안드로이드 스튜디오에서 찾지를 못 하는 거 같다. 작동해보면 아마 잘 될 거니 무시해도 좋다.

 

여기가 끝이라고 생각하고 이제 버튼을 누르는 순간... 두둥 탁

두둥탁

이렇게 오류가 빡 하고 뜨면서 강제 종료가 된다. 당황하지 말고 오류를 잘 읽어보면

java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

Theme을 다른 걸 사용하라고 한다. Theme을 변경해주자.

 

AndroidManifest.xml에 들어가서 MainActivity2(생성한 Activity)에 Theme을 추가해준다.

android:theme="@style/Theme.AppCompat.Light"

아무래도 Theme이 선언되어있지 않아서 안드로이드에서 default Theme을 적용하는 것으로 보인다.

https://stackoverflow.com/questions/27277050/android-default-theme

 

Android default theme

I am making one android application but i was thinking about themes.. If i don't declare a theme of my Android application which theme will be used? Where i can find this information? What is the

stackoverflow.com

 

 

그리고 다시 실행하면 잘 될 것이다.

 


오류, 지적사항 그리고 궁금한 것은 댓글 부탁드립니다.

728x90