Skip to main content

Overview

Aradia provides powerful search and browsing capabilities for discovering audiobooks from the 20,000+ LibriVox/Archive.org catalog. Search by title, author, or subject with multi-language support and genre filtering.

Search Features

Multi-Field Search

Search by title, author, or subject tags

Multi-Term Queries

Use comma-separated terms for OR queries

Infinite Scroll

Automatic pagination as you scroll

Real-Time Results

Instant search results from Archive.org

Search Interface

The search screen provides an intuitive interface with filter chips:
Column(
  children: [
    // Search bar
    Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(25),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withValues(alpha: 0.15),
            blurRadius: 8,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: _searchController,
              decoration: InputDecoration(
                hintText: _getHintText(),
                border: InputBorder.none,
              ),
              onSubmitted: (value) {
                final q = _buildSearchQuery(value);
                if (q.isNotEmpty) {
                  searchBloc.add(EventSearchIconClicked(q));
                }
              },
            ),
          ),
          GestureDetector(
            onTap: () {
              final q = _buildSearchQuery(_searchController.text);
              if (q.isNotEmpty) {
                searchBloc.add(EventSearchIconClicked(q));
              }
            },
            child: Container(
              padding: const EdgeInsets.all(10),
              decoration: BoxDecoration(
                color: AppColors.primaryColor,
                shape: BoxShape.circle,
              ),
              child: const Icon(Icons.search, color: Colors.white),
            ),
          ),
        ],
      ),
    ),
    // Filter chips...
  ],
)

Search Filters

Three search modes are available:

Multi-Term Queries

Use commas to create OR queries:
String _buildSearchQuery(String searchText) {
  final terms = searchText
      .split(',')
      .map((t) => t.trim())
      .where((t) => t.isNotEmpty)
      .toList();

  // If multiple terms, OR them
  final joined = terms.length > 1
      ? terms.join(' OR ')
      : (terms.isEmpty ? '' : terms.first);

  switch (searchFilter) {
    case 'author':
      return joined.isEmpty ? '' : 'creator:($joined)';
    case 'subject':
      return joined.isEmpty ? '' : 'subject:($joined)';
    default:
      return joined.isEmpty ? '' : 'title:($joined)';
  }
}
Example: Searching for “philosophy, ethics” in subject mode finds audiobooks tagged with either “philosophy” OR “ethics”.

Filter Chips

Visual filter selection with icons:
Row(
  children: [
    _buildFilterChip(
      icon: Icons.book,
      label: 'Title',
      selected: searchFilter == 'title',
      onSelected: (_) => setState(() => searchFilter = 'title'),
    ),
    const SizedBox(width: 8),
    _buildFilterChip(
      icon: Icons.person,
      label: 'Author',
      selected: searchFilter == 'author',
      onSelected: (_) => setState(() => searchFilter = 'author'),
    ),
    const SizedBox(width: 8),
    _buildFilterChip(
      icon: Icons.category,
      label: 'Subjects',
      selected: searchFilter == 'subject',
      onSelected: (_) => setState(() => searchFilter = 'subject'),
    ),
  ],
)

Infinite Scroll

Search results load more as you scroll:
_scrollController.addListener(() {
  if (_scrollController.position.pixels >=
          _scrollController.position.maxScrollExtent &&
      !isLoadingMore) {
    final state = searchBloc.state;
    if (state is SearchSuccess && state.audiobooks.isNotEmpty) {
      setState(() => isLoadingMore = true);
      searchBloc.add(EventLoadMoreResults(searchBloc.lastQuery ?? ''));
    }
  }
});

Loading States

if (state is SearchLoading && !isLoadingMore) {
  return const Center(
    child: CircularProgressIndicator(
      color: AppColors.primaryColor
    ),
  );
} else if (state is SearchSuccess) {
  return ListView.builder(
    controller: _scrollController,
    itemCount: state.audiobooks.length + 1,
    itemBuilder: (context, index) {
      if (index == state.audiobooks.length) {
        return isLoadingMore
            ? const Padding(
                padding: EdgeInsets.all(16),
                child: Center(
                  child: CircularProgressIndicator(),
                ),
              )
            : const SizedBox();
      }
      // Display audiobook item...
    },
  );
}

Genre Browsing

Browse audiobooks by genre with curated categories:
class GenreAudiobooksScreen extends StatelessWidget {
  final String genre;

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      child: Scaffold(
        appBar: AppBar(
          title: Text('${_capitalizeFirstLetter(genre)} Audiobooks'),
          bottom: TabBar(
            indicatorColor: AppColors.primaryColor,
            tabs: [
              Tab(text: 'Popular'),
              Tab(text: 'Weekly'),
              Tab(text: 'Latest'),
            ],
          ),
        ),
        body: TabBarView(
          children: [
            _AudiobookListView(
              genre: genre,
              listType: 'popular',
            ),
            _AudiobookListView(
              genre: genre,
              listType: 'popularWeekly',
            ),
            _AudiobookListView(
              genre: genre,
              listType: 'latest',
            ),
          ],
        ),
      ),
    );
  }
}

Available Genres

  • Adventure & Exploration
  • Biography & Memoir
  • Children’s Literature
  • Comedy & Humor
  • Crime & Mystery
  • Fantasy & Mythology
  • Horror & Supernatural
  • Philosophy & Ethics
  • Poetry
  • Religion & Spirituality
  • Romance & Love Stories
  • Science Fiction
  • War & Military History

Home Screen Integration

The home screen displays curated audiobook sections:
Widget _buildFeaturedSections() {
  return Column(
    children: [
      MyAudiobooks(
        title: 'Popular All Time',
        homeBloc: _popularBloc,
        fetchType: AudiobooksFetchType.popular,
        scrollController: _popularCtrl,
      ),
      MyAudiobooks(
        title: 'Trending This Week',
        homeBloc: _trendingBloc,
        fetchType: AudiobooksFetchType.popularOfWeek,
        scrollController: _trendingCtrl,
      ),
    ],
  );
}

Personalized Recommendations

FutureBuilder<String>(
  future: _recommendedGenresFuture,
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.done &&
        snapshot.hasData &&
        snapshot.data != null &&
        snapshot.data!.isNotEmpty) {
      return _buildLazyLoadSection(
        context,
        'Recommended for you',
        snapshot.data!,
      );
    }
    return const Center(
      child: Padding(
        padding: EdgeInsets.all(16),
        child: CircularProgressIndicator(
          color: AppColors.primaryColor,
        ),
      ),
    );
  },
)
Recommendations are based on your listening history and favorite genres.

Search Results Display

Results are displayed in a card list with cover art:
Card(
  elevation: 2,
  margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
  shape: RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(12),
  ),
  child: ListTile(
    contentPadding: const EdgeInsets.all(8),
    leading: ClipRRect(
      borderRadius: BorderRadius.circular(8),
      child: LowAndHighImage(
        lowQImage: audiobook.lowQCoverImage,
        highQImage: audiobook.lowQCoverImage,
        width: 60,
        height: 60,
      ),
    ),
    title: Text(
      audiobook.title,
      style: const TextStyle(fontWeight: FontWeight.bold),
    ),
    subtitle: Text(
      audiobook.author ?? 'Unknown Author',
      style: TextStyle(color: Colors.grey.shade600),
    ),
    onTap: () {
      context.push(
        '/audiobook-details',
        extra: {
          'audiobook': audiobook,
          'isDownload': false,
          'isYoutube': false,
          'isLocal': false,
        },
      );
    },
  ),
)

Error Handling

BlocConsumer<SearchBloc, SearchState>(
  listener: (context, state) {
    if (state is SearchFailure) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(state.errorMessage),
          backgroundColor: Colors.red.shade300,
          behavior: SnackBarBehavior.floating,
        ),
      );
    }
  },
  // ...
)
Search requires an active internet connection to query the Archive.org API.
Search results include full metadata: title, author, description, runtime, ratings, and cover art!

Related

Learn more about the audiobook library and metadata

Build docs developers (and LLMs) love