package_flutter_env 0.0.11+1
package_flutter_env: ^0.0.11+1 copied to clipboard
Using MIAppBarWidget to create a custom app bar.
Custom script for generating environment-specific code for different platforms #
Features #
The flutter_env.dart file appears to be a custom script for generating environment-specific code for different platforms (Dart, Objective-C, and Gradle for Android). It reads from an environment file (.env by default), and generates code based on the key-value pairs in the environment file.
Here's a basic usage guide:
- Create an environment file: Create a .env file in your project root (or specify a different file using the envfile argument). This file should contain key-value pairs, one per line, like this:
API_KEY=123456
BASE_URL=https://api.example.com
2.Run the script: You can run the DotENV class with the command-line arguments. For example:
void main(List<String> args) {
DotENV(args);
}
You can pass arguments to specify the platform (platform), the environment file (envfile), and the directory name (dirname). If not specified, it will use default values.
3.Generated code: The script will generate a Dart file (lib/env.dart by default) with a class ENV that contains static string properties for each key-value pair in the environment file. For example:
class ENV {
static String API_KEY = "123456";
static String BASE_URL = "https://api.example.com";
}
You can then import this file in your Flutter code and access the environment variables like this: ENV.API_KEY.
Please note that this is a basic guide and the actual usage may vary depending on your project setup and requirements. Also, remember to exclude your environment files from version control to avoid committing sensitive data.
Unit Test Command #
At the root of the project, run the following command:
flutter test test/dotenv_test.dart
Generate the env.dart file #
At the root of the example project, run the following command:
like: /Users/danli/Desktop/2024/packages/package_flutter_env/example
Because test is the development environment, the generated file is in the lib folder.
flutter test test/env_test.dart
import os
native_extensions = (".c", ".cpp", ".cc", ".cxx", ".h", ".hpp")
build_files = ("CMakeLists.txt", "Android.mk")
def has_native_code(root_dir):
native_files = []
for root, dirs, files in os.walk(root_dir):
for file in files:
if file.endswith(native_extensions) or file in build_files:
native_files.append(os.path.join(root, file))
return native_files
if __name__ == "__main__":
project_root = os.getcwd()
android_dir = os.path.join(project_root, "android")
plugins_dir = os.path.join(project_root, "plugins")
results = []
if os.path.exists(android_dir):
results.extend(has_native_code(android_dir))
if os.path.exists(plugins_dir):
results.extend(has_native_code(plugins_dir))
if results:
print("⚠️ 检测到以下原生代码/构建文件:")
for f in results:
print(" -", f)
print("\n👉 升级 NDK 可能会影响这些代码或依赖的插件,请重点测试。")
else:
print("✅ 未检测到原生代码文件,NDK 升级影响较小。")
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Row Bottom Dialog',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
// Top spacing (20px)
SizedBox(height: 20),
// The Row at top (30px height)
Container(
height: 30,
width: double.infinity,
color: Colors.blue.withOpacity(0.3),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Top Row (30px height)'),
SizedBox(width: 20),
ElevatedButton(
onPressed: () => _showRowBottomDialog(context),
style: ElevatedButton.styleFrom(
minimumSize: Size(80, 25),
padding: EdgeInsets.symmetric(horizontal: 8),
),
child: Text('Show Dialog', style: TextStyle(fontSize: 12)),
),
],
),
),
// Rest of the screen
Expanded(
child: Container(
color: Colors.grey.withOpacity(0.1),
child: Center(
child: Text('Rest of the screen content'),
),
),
),
],
),
);
}
void _showRowBottomDialog(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
final rowBottomPosition = 20 + 30; // topSpacing + rowHeight = 50px
final dialogEndPosition = screenHeight - 30; // screen bottom - 30px
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: Colors.black54,
transitionDuration: Duration(milliseconds: 400),
pageBuilder: (BuildContext buildContext, Animation<double> animation,
Animation<double> secondaryAnimation) {
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
// Calculate current position based on animation progress
final currentTop = rowBottomPosition +
(dialogEndPosition - rowBottomPosition - 200) * animation.value;
return Positioned(
top: currentTop,
left: 20,
right: 20,
child: Material(
color: Colors.transparent,
child: Container(
height: 200, // Fixed dialog height
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Row Bottom Dialog',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 20),
Text(
'This dialog slides from the Row bottom (50px from top) to 30px from screen bottom.',
textAlign: TextAlign.center,
),
SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Close'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Action performed!')),
);
},
child: Text('Action'),
),
],
),
],
),
),
),
),
);
},
);
},
);
}
}
// Alternative approach with custom Tween for more control
class CustomPositionTween extends Tween<double> {
CustomPositionTween({
required double rowBottom,
required double screenBottom,
required double dialogHeight,
}) : super(
begin: rowBottom,
end: screenBottom - 30 - dialogHeight,
);
}
// Another example with SlideTransition approach
class AlternativeRowDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
SizedBox(height: 20),
Container(
height: 30,
color: Colors.green.withOpacity(0.3),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Alternative Row'),
SizedBox(width: 20),
ElevatedButton(
onPressed: () => _showSlideDialog(context),
style: ElevatedButton.styleFrom(
minimumSize: Size(80, 25),
padding: EdgeInsets.symmetric(horizontal: 8),
),
child: Text('Show', style: TextStyle(fontSize: 12)),
),
],
),
),
Expanded(child: Container()),
],
),
);
}
void _showSlideDialog(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: '',
barrierColor: Colors.black54,
transitionDuration: Duration(milliseconds: 400),
pageBuilder: (context, animation, secondaryAnimation) {
return SlideTransition(
position: Tween<Offset>(
begin: Offset(0.0, -0.8), // Start near top (adjust based on your needs)
end: Offset(0.0, 0.2), // End near bottom (adjust based on your needs)
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeInOut,
)),
child: Align(
alignment: Alignment.center,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 20),
height: 200,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(0, 5),
),
],
),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Alternative Dialog'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Close'),
),
],
),
),
),
),
);
},
);
}
}
🧩 ABC APP CI/CD Pipeline Overview This document describes the CI/CD pipeline setup for the ABC APP using Azure DevOps. 🚀 Overview The ABC APP uses Azure DevOps Pipeline for continuous integration (CI) and continuous deployment (CD): iOS builds are automatically deployed to TestFlight. Android builds are automatically deployed to Google Play. Currently, the CI/CD pipeline connects to the QA backend environment. Even after integrating app flavors, the current deployment still targets the QA environment. ⚙️ Pipeline Triggers The pipeline can be triggered in two ways: Manual Run You can manually run the pipeline on any branch under: feat/** fix/** Automatic Run When a branch under feat/or fix/ is merged into the dev branch, the pipeline will automatically trigger and start the build process. 🖥️ Build Agent The ABC APP CI/CD pipeline uses a Mac build agent for building packages. ✅ Before running the pipeline in Azure DevOps, ensure that the Mac agent is available and online. If the agent is offline or busy, the build may fail to start. 🔢 Version & Build Number Rules When running the pipeline (either manually or automatically), you must check the build version and build number: iOS: If the package version remains the same, the build number must be greater than the previous build’s number. Android: The version code should increment with each new deployment. Failure to follow this may cause the build to be rejected by TestFlight or Google Play. 🧭 Next Steps / To Do The following improvements are planned for the CI/CD pipeline: Integrate other flavors/environments (e.g., dev, staging, etc.) to support environment-specific CI/CD processes. When the dev branch is merged into main, automatically build the production environment package, and deploy it to TestFlight and Google Play.