Planty is a mobile application that leverages Artificial Intelligence (AI) and Computer Vision to help farmers and agricultural specialists diagnose plant diseases instantly. By simply taking a picture of a plant leaf, users receive an instant diagnosis and the ability to purchase agricultural products directly through the app.
Screenshot 1 | Screenshot 2 |
---|---|
- Take a picture or upload an image of a plant.
- AI detects plant diseases, pests, or nutrient deficiencies.
- Provides instant results with treatment recommendations.
- Buy recommended pesticides, fertilizers, and treatments.
- Admin dashboard for managing products & orders
- View past scans and AI-generated reports.
- Track disease trends over time.
- Farmers & Growers looking for fast disease diagnosis.
- Agricultural Specialists needing AI-powered insights.
- Agriculture Companies selling pesticides and fertilizers.
The application follows Riverpod/Reference Architecture:
- Presentation Layer: Widgets, Screens, Controllers (Notifiers & Providers) for managing the state
- Data Layer: Repositories and Data sources
- Domain Layer: Models and business entities
|- assets <- images & app icon
|
|- lib
|
|_ ๐src
|
|__ ๐core <- (constants, theme, shared widgets, utils)
|
|__ ๐auth
| |
| |__ ๐data <- (auth repository: sign in, sign out, getAccount, verifyOTP, etc.)
| |
| |__ ๐presentation <- (controllers for managing state with Riverpod 2.0 & Screens/Widgets)
|
|__ ๐scan
| |
| |__ ๐data <- (scan repository: save scan, get scan, get scans, save & delete access tokens)
| |
| |__ ๐domain <- (entities: PlantScanResponse, ScanResult, InputData, PlantStatus, Disease, etc.)
| |
| |__ ๐presentation <- (controllers for managing state with Riverpod 2.0 & Screens/Widgets)
|
|__ ๐onboarding
| |
| |__ ๐domain <- (Onboarding Content)
| |
| |__ ๐presentation <- (Onboarding & Welcome Screens)
|
|__ ๐market
| |
| |__ ๐data <- (AppwriteMarketRepository & IMarketplaceService: manage products, carts, and orders)
| |
| |__ ๐domain <- (entities: Product, CartItem, Order, OrderItem)
| |
| |__ ๐presentation <- (controllers for managing state with Riverpod 2.0 & Screens/Widgets)
|
|__ ๐admin
| |
| |__ ๐data <- (IAdminService & AppwriteAdminRepository: manage products & orders, edit, add, or delete them)
| |
| |__ ๐presentation <- (controllers for managing state with Riverpod 2.0 & Screens/Widgets)
git clone https://github.com/Dev-Salem/plant_app
2๏ธโฃ Install Dependencies & configure secrets using envied
flutter pub get
Create .env at the root director with these values:
ENDPOINT=https://cloud.appwrite.io/v1
PROJECT_ID=project-id-from-appwrite-console
flutter run
- Sign in for Appwrite
- Create project & follow the instruction
- Create collections using Python SDK with this script:
Python script for creating collections
import logging
import sys
from appwrite.client import Client
from appwrite.services.databases import Databases
# Set up logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("appwrite_setup.log"),
logging.StreamHandler(sys.stdout),
],
)
logger = logging.getLogger("AppwriteSetup")
# Initialize Appwrite client
client = Client()
client.set_endpoint("https://cloud.appwrite.io/v1")
client.set_project("")
client.set_key(
""
)
# Connect to database service
databases = Databases(client)
DATABASE_ID = "planty-db-id"
def create_products_collection():
logger.info("๐ Creating products collection...")
try:
# Check if collection exists
try:
collection = databases.get_collection(DATABASE_ID, "products")
logger.info(" โ Products collection already exists")
except:
# Create the collection
collection = databases.create_collection(
database_id=DATABASE_ID, collection_id="products", name="Products"
)
logger.info(" โ Products collection created")
# Create attributes based on the Product entity
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="products",
key="name",
size=255,
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="products",
key="description",
size=10000,
required=True,
)
databases.create_float_attribute(
database_id=DATABASE_ID,
collection_id="products",
key="price",
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="products",
key="imageUrl",
size=2048,
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="products",
key="category",
size=100,
required=True,
default="Other",
)
databases.create_boolean_attribute(
database_id=DATABASE_ID,
collection_id="products",
key="isAvailable",
required=True,
default=True,
)
# Create indexes
databases.create_index(
database_id=DATABASE_ID,
collection_id="products",
key="name_index",
type="fulltext",
attributes=["name"],
)
databases.create_index(
database_id=DATABASE_ID,
collection_id="products",
key="category_index",
type="key",
attributes=["category"],
)
logger.info("โ
Products collection setup complete")
return True
except Exception as e:
logger.error(f"โ Error creating products collection: {str(e)}")
return False
def create_cart_items_collection():
logger.info("๐ Creating cart_items collection...")
try:
# Check if collection exists
try:
collection = databases.get_collection(DATABASE_ID, "cart_items")
logger.info(" โ Cart items collection already exists")
except:
# Create the collection
collection = databases.create_collection(
database_id=DATABASE_ID, collection_id="cart_items", name="Cart Items"
)
logger.info(" โ Cart items collection created")
# Create attributes based on the CartItem entity
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="cart_items",
key="userId",
size=100,
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="cart_items",
key="productId",
size=100,
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="cart_items",
key="productName",
size=255,
required=True,
)
databases.create_float_attribute(
database_id=DATABASE_ID,
collection_id="cart_items",
key="price",
required=True,
)
databases.create_integer_attribute(
database_id=DATABASE_ID,
collection_id="cart_items",
key="quantity",
required=True,
min=1,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="cart_items",
key="imageUrl",
size=2048,
required=True,
)
# Create indexes
databases.create_index(
database_id=DATABASE_ID,
collection_id="cart_items",
key="userId_index",
type="key",
attributes=["userId"],
)
databases.create_index(
database_id=DATABASE_ID,
collection_id="cart_items",
key="user_product_index",
type="key",
attributes=["userId", "productId"],
)
logger.info("โ
Cart items collection setup complete")
return True
except Exception as e:
logger.error(f"โ Error creating cart items collection: {str(e)}")
return False
def create_orders_collection():
logger.info("๐ Creating orders collection...")
try:
# Check if collection exists
try:
collection = databases.get_collection(DATABASE_ID, "orders")
logger.info(" โ Orders collection already exists")
except:
# Create the collection
collection = databases.create_collection(
database_id=DATABASE_ID, collection_id="orders", name="Orders"
)
logger.info(" โ Orders collection created")
# Create attributes based on the Order entity
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="orders",
key="userId",
size=100,
required=True,
)
databases.create_float_attribute(
database_id=DATABASE_ID,
collection_id="orders",
key="totalAmount",
required=True,
)
databases.create_datetime_attribute(
database_id=DATABASE_ID,
collection_id="orders",
key="dateTime",
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="orders",
key="status",
size=50,
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="orders",
key="address",
size=1000,
required=False,
)
# Create indexes
databases.create_index(
database_id=DATABASE_ID,
collection_id="orders",
key="userId_index",
type="key",
attributes=["userId"],
)
databases.create_index(
database_id=DATABASE_ID,
collection_id="orders",
key="status_index",
type="key",
attributes=["status"],
)
databases.create_index(
database_id=DATABASE_ID,
collection_id="orders",
key="dateTime_index",
type="key",
attributes=["dateTime"],
)
logger.info("โ
Orders collection setup complete")
return True
except Exception as e:
logger.error(f"โ Error creating orders collection: {str(e)}")
return False
def create_order_items_collection():
logger.info("๐ Creating order_items collection...")
try:
# Check if collection exists
try:
collection = databases.get_collection(DATABASE_ID, "order_items")
logger.info(" โ Order items collection already exists")
except:
# Create the collection
collection = databases.create_collection(
database_id=DATABASE_ID, collection_id="order_items", name="Order Items"
)
logger.info(" โ Order items collection created")
# Create attributes based on the OrderItem entity
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="order_items",
key="orderId",
size=100,
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="order_items",
key="productId",
size=100,
required=True,
)
databases.create_string_attribute(
database_id=DATABASE_ID,
collection_id="order_items",
key="productName",
size=255,
required=True,
)
databases.create_float_attribute(
database_id=DATABASE_ID,
collection_id="order_items",
key="price",
required=True,
)
databases.create_integer_attribute(
database_id=DATABASE_ID,
collection_id="order_items",
key="quantity",
required=True,
min=1,
)
# Create indexes
databases.create_index(
database_id=DATABASE_ID,
collection_id="order_items",
key="orderId_index",
type="key",
attributes=["orderId"],
)
logger.info("โ
Order items collection setup complete")
return True
except Exception as e:
logger.error(f"โ Error creating order items collection: {str(e)}")
return False
if __name__ == "__main__":
logger.info("๐ Starting Appwrite Plant App setup script...")
# Create all collections
products_result = create_products_collection()
cart_items_result = create_cart_items_collection()
orders_result = create_orders_collection()
order_items_result = create_order_items_collection()
# Print summary
logger.info("\n๐ SETUP SUMMARY:")
logger.info(
f"Products collection: {'โ
SUCCESS' if products_result else 'โ FAILED'}"
)
logger.info(
f"Cart items collection: {'โ
SUCCESS' if cart_items_result else 'โ FAILED'}"
)
logger.info(f"Orders collection: {'โ
SUCCESS' if orders_result else 'โ FAILED'}")
logger.info(
f"Order items collection: {'โ
SUCCESS' if order_items_result else 'โ FAILED'}"
)
if all([products_result, cart_items_result, orders_result, order_items_result]):
logger.info("\n๐ All collections created successfully!")
else:
logger.warning("\nโ ๏ธ Some collections have issues. Check the logs for details.")
- Deploy the cloud function that connects with Crop API and the one that gets the user history (clone the function and deploy it from the console) Getting past diagnoses Creating a diagnose
- Freemium Model: Free basic scans with premium features (e.g., detailed analysis).
- Marketplace Commission: Earn from product sales via partnerships with suppliers.
- Subscription Plans: Offer advanced AI-based insights for professional farmers.
- โ๏ธ Offline Mode โ Enable diagnosis without internet.
- โ๏ธ Community Forum โ Allow farmers to discuss issues.
- โ๏ธ Multi-language Support โ Expand to other languages.
- The demo is deployed on github pages and is not meant for web usage
- images might not render because CORS issues with Crop API
- There might be issues with other browsers other than Chrome
- Payment for the mini market place isn't configured
- Legal documents (like Privacy & Policy, Terms Of Use) are not presented