Application Logo Update Without App new build

Development Mode 

--------------------------------------------------------------------------------------------------------------------------------

AndroidManifest.xml (debug)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application>
        <activity
            android:name=".MainActivity"
            android:exported="true"
            tools:replace="android:exported">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

AndroidManifest.xml (main)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <application
        android:label="application_logo"
        android:name="${applicationName}">

        <activity
            android:name=".MainActivity"
            android:exported="false"
            android:launchMode="singleTop"
            android:taskAffinity=""
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
        </activity>

        <activity-alias
            android:name=".MainActivityDefault"
            android:enabled="true"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_default"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityDiwali"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_diwali"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityPremium"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_premium"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>

        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

    </application>

    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>

</manifest>

Main.dart

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

const platform = MethodChannel('com.example.application_logo/icon');

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Supabase.initialize(
    url: '',
    publishableKey:
        '',
  );

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(title: 'Icon Switch Demo', home: const IconTestScreen());
  }
}

class IconTestScreen extends StatefulWidget {
  const IconTestScreen({super.key});

  @override
  State<IconTestScreen> createState() => _IconTestScreenState();
}

class _IconTestScreenState extends State<IconTestScreen> {
  String _status = 'Idle';
  String _currentIcon = 'unknown';

  @override
  void initState() {
    super.initState();
    _loadIconFromBackend();
  }

  // Simulates the automatic startup flow: fetch from Supabase,
  // then switch icon automatically.
  Future<void> _loadIconFromBackend() async {
    try {
      final data = await Supabase.instance.client
          .from('app_config')
          .select('value')
          .eq('key', 'active_icon')
          .single();

      final iconName = data['value'] as String;
      await _switchIcon(iconName, fromBackend: true);
    } catch (e) {
      setState(() => _status = 'Backend fetch failed: $e');
    }
  }

  // Manual trigger, useful for testing each icon without
  // touching the Supabase dashboard every time.
  Future<void> _switchIcon(String iconKey, {bool fromBackend = false}) async {
    try {
      final result = await platform.invokeMethod('switchIcon', {
        'icon': iconKey,
      });
      setState(() {
        _status = fromBackend
            ? 'Auto-switched from backend: $result'
            : 'Manually switched: $result';
        _currentIcon = iconKey;
      });
    } on PlatformException catch (e) {
      setState(() => _status = 'Switch failed: ${e.message}');
    }
  }

  Future<void> _checkCurrentIcon() async {
    try {
      final result = await platform.invokeMethod('getCurrentIcon');
      setState(() => _status = 'Currently active icon: $result');
    } on PlatformException catch (e) {
      setState(() => _status = 'Check failed: ${e.message}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('App Icon Switch — Dev Mode')),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text('Status:\n$_status', style: const TextStyle(fontSize: 16)),
            const SizedBox(height: 8),
            Text('Selected key: $_currentIcon'),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: () => _switchIcon('default'),
              child: const Text('Switch to Default Icon'),
            ),
            const SizedBox(height: 8),
            ElevatedButton(
              onPressed: () => _switchIcon('diwali'),
              child: const Text('Switch to Diwali Icon'),
            ),
            const SizedBox(height: 8),
            ElevatedButton(
              onPressed: () => _switchIcon('premium'),
              child: const Text('Switch to Premium Icon'),
            ),
            const SizedBox(height: 24),
            OutlinedButton(
              onPressed: _checkCurrentIcon,
              child: const Text('Check Current Active Icon'),
            ),
            const SizedBox(height: 8),
            OutlinedButton(
              onPressed: _loadIconFromBackend,
              child: const Text('Re-fetch From Supabase'),
            ),
          ],
        ),
      ),
    );
  }
}


===================================================================================================================================================================================================


Release Mode

--------------------------------------------------------------

AndroidManifest.xml (main)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <application
        android:label="application_logo"
        android:name="${applicationName}">

        <activity
            android:name=".MainActivity"
            android:exported="false"
            android:launchMode="singleTop"
            android:taskAffinity=""
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
        </activity>

        <activity-alias
            android:name=".MainActivityDefault"
            android:enabled="true"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_default"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityDiwali"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_diwali"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>

        <activity-alias
            android:name=".MainActivityPremium"
            android:enabled="false"
            android:exported="true"
            android:icon="@mipmap/ic_launcher_premium"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>

        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

    </application>

    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>

</manifest>

MainActivity.kt

--------------------------------------------------------------------------------------------------------------

package com.example.application_logo

import android.content.ComponentName

import android.content.pm.PackageManager

import io.flutter.embedding.android.FlutterActivity

import io.flutter.embedding.engine.FlutterEngine

import io.flutter.plugin.common.MethodChannel


class MainActivity : FlutterActivity() {

    private val CHANNEL = "com.example.application_logo/icon"

    // Maps friendly icon keys to alias component names.

    // packageName is resolved automatically, so this works

    // even if you rename your applicationId later.

    private val aliasMap: Map<String, String>

        get() = mapOf(

            "default" to "$packageName.MainActivityDefault",

            "diwali"  to "$packageName.MainActivityDiwali",

            "premium" to "$packageName.MainActivityPremium"

        )


    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {

        super.configureFlutterEngine(flutterEngine)


        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)

            .setMethodCallHandler { call, result ->

                when (call.method) {

                    "switchIcon" -> {

                        val icon = call.argument<String>("icon") ?: "default"

                        val success = switchIcon(icon)

                        if (success) {

                            result.success("Icon switched to $icon")

                        } else {

                            result.error("INVALID_ICON", "Unknown icon key: $icon", null)

                        }

                    }

                    "getCurrentIcon" -> {

                        result.success(getCurrentEnabledIcon())

                    }

                    else -> result.notImplemented()

                }

            }

    }


    /**

     * Enables the target alias and disables all others.

     * Returns false if the icon key is not recognized.

     */

    private fun switchIcon(iconKey: String): Boolean {

        val map = aliasMap

        val targetAlias = map[iconKey] ?: return false

        val pm = packageManager


        map.values.forEach { alias ->

            val state = if (alias == targetAlias)

                PackageManager.COMPONENT_ENABLED_STATE_ENABLED

            else

                PackageManager.COMPONENT_ENABLED_STATE_DISABLED


            pm.setComponentEnabledSetting(

                ComponentName(this, alias),

                state,

                PackageManager.DONT_KILL_APP

            )

        }

        return true

    }


    /**

     * Returns which icon key is currently active, useful for

     * debugging / confirming state without adb.

     */

    private fun getCurrentEnabledIcon(): String {

        val pm = packageManager

        for ((key, alias) in aliasMap) {

            val state = pm.getComponentEnabledSetting(ComponentName(this, alias))

            val isEnabled = state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED ||

                    (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && key == "default")

            if (isEnabled) return key

        }

        return "unknown"

    }

}

---------------------------------------------------------------------------------------------------------------

Main.dart

---------------------------------------------------------------------------------------------------------------

import 'package:flutter/material.dart';

import 'package:flutter/services.dart';

import 'package:supabase_flutter/supabase_flutter.dart';


const platform = MethodChannel('com.example.application_logo/icon');


Future<void> main() async {

  WidgetsFlutterBinding.ensureInitialized();


  await Supabase.initialize(

    url: 'https://YOUR-PROJECT.supabase.co',

    anonKey: 'YOUR-ANON-KEY',

  );


  runApp(const MyApp());

}


class MyApp extends StatelessWidget {

  const MyApp({super.key});


  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Icon Switch Demo',

      home: const IconTestScreen(),

    );

  }

}


class IconTestScreen extends StatefulWidget {

  const IconTestScreen({super.key});


  @override

  State<IconTestScreen> createState() => _IconTestScreenState();

}


class _IconTestScreenState extends State<IconTestScreen> {

  String _status = 'Idle';

  String _currentIcon = 'unknown';


  @override

  void initState() {

    super.initState();

    _loadIconFromBackend();

  }


  // Simulates the automatic startup flow: fetch from Supabase,

  // then switch icon automatically.

  Future<void> _loadIconFromBackend() async {

    try {

      final data = await Supabase.instance.client

          .from('app_config')

          .select('value')

          .eq('key', 'active_icon')

          .single();


      final iconName = data['value'] as String;

      await _switchIcon(iconName, fromBackend: true);

    } catch (e) {

      setState(() => _status = 'Backend fetch failed: $e');

    }

  }


  // Manual trigger, useful for testing each icon without

  // touching the Supabase dashboard every time.

  Future<void> _switchIcon(String iconKey, {bool fromBackend = false}) async {

    try {

      final result = await platform.invokeMethod('switchIcon', {'icon': iconKey});

      setState(() {

        _status = fromBackend

            ? 'Auto-switched from backend: $result'

            : 'Manually switched: $result';

        _currentIcon = iconKey;

      });

    } on PlatformException catch (e) {

      setState(() => _status = 'Switch failed: ${e.message}');

    }

  }


  Future<void> _checkCurrentIcon() async {

    try {

      final result = await platform.invokeMethod('getCurrentIcon');

      setState(() => _status = 'Currently active icon: $result');

    } on PlatformException catch (e) {

      setState(() => _status = 'Check failed: ${e.message}');

    }

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: const Text('App Icon Switch — Dev Mode')),

      body: Padding(

        padding: const EdgeInsets.all(20),

        child: Column(

          crossAxisAlignment: CrossAxisAlignment.stretch,

          children: [

            Text('Status:\n$_status', style: const TextStyle(fontSize: 16)),

            const SizedBox(height: 8),

            Text('Selected key: $_currentIcon'),

            const SizedBox(height: 24),

            ElevatedButton(

              onPressed: () => _switchIcon('default'),

              child: const Text('Switch to Default Icon'),

            ),

            const SizedBox(height: 8),

            ElevatedButton(

              onPressed: () => _switchIcon('diwali'),

              child: const Text('Switch to Diwali Icon'),

            ),

            const SizedBox(height: 8),

            ElevatedButton(

              onPressed: () => _switchIcon('premium'),

              child: const Text('Switch to Premium Icon'),

            ),

            const SizedBox(height: 24),

            OutlinedButton(

              onPressed: _checkCurrentIcon,

              child: const Text('Check Current Active Icon'),

            ),

            const SizedBox(height: 8),

            OutlinedButton(

              onPressed: _loadIconFromBackend,

              child: const Text('Re-fetch From Supabase'),

            ),

          ],

        ),

      ),

    );

  }

}




















Comments

Popular posts from this blog

A Nxt Privacy Policy

Illustrator Guides

NXT chat policy