gemini_langchain 0.0.2 copy "gemini_langchain: ^0.0.2" to clipboard
gemini_langchain: ^0.0.2 copied to clipboard

A Dart/Flutter package that seamlessly connects LangChain’s prompt templating with Gemini AI’s streaming API

example/lib/main.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:gemini_langchain/gemini_langchain.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  GeminiClient.initialize(apiKey: 'your_api_key_here');

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Gemini + LangChain Demo',
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: Colors.black,
        appBarTheme: const AppBarTheme(
          backgroundColor: Color(0xFF1F1F1F),
          centerTitle: true,
          elevation: 0,
          titleTextStyle: TextStyle(
            fontSize: 22,
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
        inputDecorationTheme: InputDecorationTheme(
          filled: true,
          fillColor: Colors.grey.shade900,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(12),
            borderSide: BorderSide.none,
          ),
          hintStyle: TextStyle(color: Colors.grey.shade500),
          labelStyle: const TextStyle(color: Colors.white70),
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.blueAccent,
            minimumSize: const Size.fromHeight(50),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12),
            ),
            textStyle: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
        ),
      ),
      home: const ProductsPage(),
    );
  }
}

class Product {
  final int id;
  final String title;
  final String description;
  final double price;
  final double rating;

  Product({
    required this.id,
    required this.title,
    required this.description,
    required this.price,
    required this.rating,
  });

  factory Product.fromJson(Map<String, dynamic> json) => Product(
    id: json['id'],
    title: json['title'],
    description: json['description'],
    price: (json['price'] as num).toDouble(),
    rating: (json['rating'] as num).toDouble(),
  );
}

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

  @override
  State<ProductsPage> createState() => _ProductsPageState();
}

class _ProductsPageState extends State<ProductsPage> {
  List<Product> products = [];
  String? errorMessage;
  bool isLoading = false;

  final TextEditingController _questionController = TextEditingController();

  String aiResponse = '';
  bool isAIloading = false;

  final LangChain _langChain = LangChain(
    template: '''
You are a helpful shopping assistant.

Here are the products:
{product_list}

Answer the following customer question:
{question}
''',
  );

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

  Future<void> fetchProducts() async {
    setState(() {
      isLoading = true;
      errorMessage = null;
    });
    try {
      final response = await http.get(Uri.parse('https://dummyjson.com/products'));
      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        final List items = data['products'];
        setState(() {
          products = items.map((json) => Product.fromJson(json)).toList();
          isLoading = false;
        });
      } else {
        throw Exception('Failed to load products');
      }
    } catch (e) {
      setState(() {
        errorMessage = e.toString();
        isLoading = false;
      });
    }
  }

  Future<void> askAI() async {
    if (products.isEmpty || _questionController.text.trim().isEmpty) return;

    final productListStr = products
        .map(
          (p) =>
      '${p.title} (Price: \$${p.price.toStringAsFixed(2)}, Rating: ${p.rating.toStringAsFixed(1)})',
    )
        .join('\n');

    final variables = {
      'product_list': productListStr,
      'question': _questionController.text.trim(),
    };

    setState(() {
      aiResponse = '';
      isAIloading = true;
    });

    try {
      await for (final chunk in _langChain.run(variables)) {
        setState(() {
          aiResponse = chunk;
        });
      }
    } catch (e) {
      setState(() {
        aiResponse = 'Error: $e';
      });
    } finally {
      setState(() {
        isAIloading = false;
      });
    }
  }

  @override
  void dispose() {
    _questionController.dispose();
    super.dispose();
  }

  Widget _buildProductItem(Product p) {
    return Card(
      color: Colors.grey.shade900,
      margin: const EdgeInsets.symmetric(vertical: 6),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: ListTile(
        contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
        title: Text(
          p.title,
          style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
        ),
        subtitle: Padding(
          padding: const EdgeInsets.only(top: 6),
          child: Text(
            p.description,
            maxLines: 2,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(color: Colors.grey.shade400),
          ),
        ),
        trailing: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.end,
          children: [
            Text('\$${p.price.toStringAsFixed(2)}',
                style: const TextStyle(
                    color: Colors.greenAccent, fontWeight: FontWeight.bold)),
            const SizedBox(height: 6),
            Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Icon(Icons.star, size: 16, color: Colors.amber),
                const SizedBox(width: 4),
                Text(
                  p.rating.toStringAsFixed(1),
                  style: const TextStyle(color: Colors.amber),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Gemini + LangChain Demo')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: isLoading
            ? const Center(child: CircularProgressIndicator())
            : errorMessage != null
            ? Center(
          child: Text(
            'Error: $errorMessage',
            style: const TextStyle(color: Colors.redAccent),
          ),
        )
            : Column(
          children: [
            Expanded(
              flex: 5,
              child: ListView.builder(
                itemCount: products.length,
                itemBuilder: (context, index) {
                  return _buildProductItem(products[index]);
                },
              ),
            ),
            const SizedBox(height: 12),
            TextField(
              controller: _questionController,
              decoration: const InputDecoration(
                labelText: 'Ask AI about these products',
                hintText: 'Type your question here...',
              ),
              minLines: 1,
              maxLines: 3,
              style: const TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 12),
            ElevatedButton.icon(
              icon: isAIloading
                  ? const SizedBox(
                height: 20,
                width: 20,
                child: CircularProgressIndicator(
                  strokeWidth: 2,
                  color: Colors.white,
                ),
              )
                  : const Icon(Icons.question_answer),
              label: Text(isAIloading ? 'Loading...' : 'Ask AI'),
              onPressed: isAIloading ? null : askAI,
            ),
            const SizedBox(height: 16),
            Expanded(
              flex: 3,
              child: Container(
                width: double.infinity,
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.grey.shade900,
                  borderRadius: BorderRadius.circular(12),
                ),
                child: SingleChildScrollView(
                  child: Text(
                    aiResponse,
                    style: const TextStyle(fontSize: 16),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
6
likes
135
points
14
downloads

Publisher

unverified uploader

Weekly Downloads

A Dart/Flutter package that seamlessly connects LangChain’s prompt templating with Gemini AI’s streaming API

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_gemini, http, langchain

More

Packages that depend on gemini_langchain