Coder Social home page Coder Social logo

maheshmnj / searchfield Goto Github PK

View Code? Open in Web Editor NEW
73.0 4.0 58.0 451 KB

A highly customizable simple and easy to use flutter Widget to add a searchfield to your Flutter Application.This Widget allows you to search and select from list of suggestions.

License: MIT License

Kotlin 0.06% Swift 0.20% Objective-C 0.02% Dart 97.77% HTML 1.95%
flutter flutter-package hacktoberfest widget package pubdev autocomplete

searchfield's People

Contributors

aglpy avatar andriireverchuk avatar ayushbherwani1998 avatar imgbotapp avatar kvii avatar lrsvmb avatar luckycreationsindia avatar maheshmnj avatar nbioni avatar romain-pattyn avatar tahateber95 avatar tomjfox avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

searchfield's Issues

Suggestions Menu not closing after selection

The suggestions do not close after having tapped on them, this is my code:

SearchField(
                    suggestions: [
                      "London",
                      "Paris",
                      "Kemer",
                      "Berlin",
                    ],
                    controller: departureController,
                    onTap: (value) {},
                    hasOverlay: false,
                    validator: (p0) {},
                    searchInputDecoration: InputDecoration(
                      labelText: "Abfertiger",
                      floatingLabelBehavior: FloatingLabelBehavior.always,
                      labelStyle: TextStyle(fontSize: 20, color: Colors.black),
                      border: OutlineInputBorder(
                        borderSide: BorderSide(
                          width: 2,
                          color: Colors.grey,
                        ),
                      ),
                      focusedBorder: OutlineInputBorder(
                        borderSide: BorderSide(
                          width: 2,
                          color: Colors.greenAccent,
                        ),
                      ),
                    ),
                  ),

onTap error : is not a subtype of type '((SearchFieldListItem<dynamic>) => dynamic)?'

type '(SearchFieldListItem<FullNameModel>) => int?' is not a subtype of type '((SearchFieldListItem<dynamic>) => dynamic)?'

my id is an integer.

SearchField<FullNameModel>(
                    suggestions: allSoldierFullNames == null
                        ? []
                        : allSoldierFullNames
                            .map((e) => SearchFieldListItem<FullNameModel>(e.fullName))
                            .toList(),
                    suggestionState: Suggestion.expand,
                    textInputAction: TextInputAction.done,
                    hint: 'Qəhrəmanı seçin',
                    hasOverlay: false,
                    suggestionAction: SuggestionAction.unfocus,
                    searchInputDecoration: filterFormFieldDecoration(
                      context: context,
                      prefixIcon: Icon(
                        Icons.place_outlined,
                        color: Colors.blue,
                      ),
                    ),
                    maxSuggestionsInViewPort: 6,
                    itemHeight: 45.h,
                    onTap: (value) {
                      // print(value);
                      soldierId = value.item?.id;
                      // print(soldierId);
                    },
                  ),

Loading Spinner

The problem that i was facing for my project that there was no loading spinner in it. Kindly add a loading spinner in your package.

Thanks in advance

Adding a text style to the default suggestions

When no child is given to the SearchFieldListItem objects, it uses the search key as text for the suggestion.

I would simply like to add the possibility to modify the textStyle of that text.

How to add custom sort logic for already filtered data?

filteredList.sort((a, b) =>
a.toLowerCase()
.indexOf(filter)
.compareTo(b.toLowerCase().indexOf(filter)

Can I add this kind of logic?

for example:
var streets = ["testabc","abc"];

when i will input search value "abc",
output suggestions will be : 1."testabc" 2."abc"
expected suggestions : 1."abc" 2."testabc"

so i want to output suggestions with more appropriate order by search value.

Thanks

Surface out TextFormField properties

I love your library, but I'm frustrated that I can't access the autocorrect property of the TextFormField inside the SearchField.

Please surface out this property so I can turn off the suggestive text. You might consider also including enableSuggestions of the TextFormField as well.

Thank you!

'package:flutter/src/widgets/overlay.dart': Failed assertion: line 207 pos 12: '_overlay != null': is not true.

Describe the bug
A clear and concise description of what the bug is.

Unhandled Exception: Bad state: Cannot add new events after calling close

When I used second-time SearchField, I didn't know why it gave me this error.

sorry I don't have wide info about error.

edited:
Issue reproduces with latest v0.9.9

minimal code sample
import 'package:flutter/material.dart';
import 'package:searchfield/searchfield.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Material App',
      home: BottomNavBarHome(),
    );
  }
}

class BottomNavBarHome extends StatefulWidget {
  const BottomNavBarHome({Key? key}) : super(key: key);

  @override
  State<BottomNavBarHome> createState() => _BottomNavBarHomeState();
}

class _BottomNavBarHomeState extends State<BottomNavBarHome> {
  late PageController _pageController;
  late final GlobalKey<State<BottomNavigationBar>> _bottomNavBarKey;
  DateTime? currentBackPressTime;
  @override
  void initState() {
    _pageController = PageController();
    _bottomNavBarKey = GlobalKey<State<BottomNavigationBar>>();
    super.initState();
  }

  int selectindex = 0;
  void _onItemTapped(int selectedIndex) {
    setState(() {
      selectindex = selectedIndex;
      _pageController.jumpToPage(selectindex);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Scaffold(
        extendBody: true,
        body: PageView(
          controller: _pageController,
          children: [
            PageOne(
              bottomNavBarKey: _bottomNavBarKey,
            ),
            PageSecond(),
            PageThird(),
          ],
          // onPageChanged: _onPageChanged,
        ),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: selectindex,
          type: BottomNavigationBarType.fixed,
          key: _bottomNavBarKey,
          onTap: _onItemTapped,
          items: const [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              label: 'Page 1',
              backgroundColor: Colors.blue,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.person),
              label: 'Page 2',
              backgroundColor: Colors.blue,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.book),
              label: 'Page 3',
              backgroundColor: Colors.blue,
            ),
          ],
        ),
      ),
    );
  }
}

class PageOne extends StatefulWidget {
  const PageOne({Key? key, required this.bottomNavBarKey}) : super(key: key);

  final GlobalKey<State<BottomNavigationBar>> bottomNavBarKey;

  @override
  State<PageOne> createState() => _PageOneState();
}

class _PageOneState extends State<PageOne> {
  final List<String> _suggestions = [
    'United States',
    'Germany',
    'Washington',
    'Paris',
    'Jakarta',
    'Australia',
    'India',
    'Czech Republic',
    'Lorem Ipsum',
  ];

  BottomNavigationBar? bottomNavKey;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: SearchField(
          suggestionState: Suggestion.expand,
          suggestionAction: SuggestionAction.unfocus,
          suggestions: _suggestions.map((e) => SearchFieldListItem(e)).toList(),
          textInputAction: TextInputAction.done,
          hint: 'SearchField Example 1',
          autoCorrect: false,
          maxSuggestionsInViewPort: 3,
          itemHeight: 45,
          onSuggestionTap: (x) async {
            if (widget.bottomNavBarKey.currentWidget != null) {
              bottomNavKey =
                  widget.bottomNavBarKey.currentWidget as BottomNavigationBar;
            }
            bottomNavKey?.onTap!(1);
          },
        ),
      ),
    );
  }
}

class PageSecond extends StatelessWidget {
  const PageSecond({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text('Page Second')),
    );
  }
}

class PageThird extends StatelessWidget {
  const PageThird({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
          child: Text(
        'Page Third',
      )),
    );
  }
}
logs
Restarted application in 1,876ms.
D/EGL_emulation( 6657): app_time_stats: avg=4548.26ms min=4548.26ms max=4548.26ms count=1
D/InputMethodManager( 6657): showSoftInput() view=io.flutter.embedding.android.FlutterView{6bfaeb4 VFE...... .F...... 0,0-1440,3064 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/InsetsController( 6657): show(ime(), fromIme=true)
D/EGL_emulation( 6657): app_time_stats: avg=11220.27ms min=5.66ms max=168162.59ms count=15
D/EGL_emulation( 6657): app_time_stats: avg=5991.23ms min=5991.23ms max=5991.23ms count=1
D/EGL_emulation( 6657): app_time_stats: avg=46.64ms min=5.86ms max=502.45ms count=22
D/EGL_emulation( 6657): app_time_stats: avg=314.47ms min=3.32ms max=6142.25ms count=20
D/EGL_emulation( 6657): app_time_stats: avg=5358.02ms min=5358.02ms max=5358.02ms count=1
D/InputMethodManager( 6657): showSoftInput() view=io.flutter.embedding.android.FlutterView{6bfaeb4 VFE...... .F...... 0,0-1440,3064 #1 aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
D/EGL_emulation( 6657): app_time_stats: avg=1785.92ms min=16.31ms max=8787.12ms count=5
D/InsetsController( 6657): show(ime(), fromIme=true)
D/EGL_emulation( 6657): app_time_stats: avg=613.65ms min=4.96ms max=9016.41ms count=15
D/EGL_emulation( 6657): app_time_stats: avg=66.43ms min=5.82ms max=505.28ms count=21
D/EGL_emulation( 6657): app_time_stats: avg=377.43ms min=140.98ms max=496.75ms count=3
D/EGL_emulation( 6657): app_time_stats: avg=715.01ms min=4.28ms max=13469.75ms count=19
D/EGL_emulation( 6657): app_time_stats: avg=1663.22ms min=11.73ms max=11548.72ms count=7

════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown while finalizing the widget tree:
'package:flutter/src/widgets/overlay.dart': Failed assertion: line 207 pos 12: '_overlay != null': is not true.

When the exception was thrown, this was the stack:
#2      OverlayEntry.remove (package:flutter/src/widgets/overlay.dart:207:12)
#3      _SearchFieldState.dispose (package:searchfield/src/searchfield.dart:374:22)
#4      StatefulElement.unmount (package:flutter/src/widgets/framework.dart:5867:11)
#5      _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2097:13)
#6      _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#7      SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6940:14)
#8      _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#9      _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#10     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6940:14)
#11     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#12     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#13     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:5704:14)
#14     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#15     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#16     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6940:14)
#17     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#18     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#19     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6940:14)
#20     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#21     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#22     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:5704:14)
#23     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#24     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#25     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:5704:14)
#26     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#27     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#28     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:5704:14)
#29     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#30     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#31     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:5704:14)
#32     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#33     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2095:7)
#34     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:5704:14)
#35     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2093:13)
#36     ListIterable.forEach (dart:_internal/iterable.dart:49:13)
#37     _InactiveElements._unmountAll (package:flutter/src/widgets/framework.dart:2106:25)
#38     BuildOwner.lockState (package:flutter/src/widgets/framework.dart:2799:15)
#39     BuildOwner.finalizeTree (package:flutter/src/widgets/framework.dart:3234:7)
#40     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:992:19)
#41     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:448:5)
#42     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1386:15)
#43     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1311:9)
#44     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1169:5)
#45     _invoke (dart:ui/hooks.dart:312:13)
#46     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:399:5)
#47     _drawFrame (dart:ui/hooks.dart:283:31)
(elided 2 frames from class _AssertionError)
════════════════════════════════════════════════════════════════════════════════
D/EGL_emulation( 6657): app_time_stats: avg=31784.69ms min=31784.69ms max=31784.69ms count=1

Randomly stops working when reopening the app

Describe the bug
When I close the app thru the app switcher in release mode and reopen it from the home screen, the search field stops working. When I click it and type something (e.g. "Mexico") nothing shows up.

To Reproduce
Steps to reproduce the behavior:

  1. Force close app thru app switcher
  2. Reopen app
  3. Click on search bar

Expected behavior
I should be able to search after force closing app

Actual behavior
I can't search

Code sample

Show code sample
SearchField(
                controller: numController,
                suggestions:
                    cnt.map((e) => SearchFieldListItem(e)).toList(),
                suggestionState: Suggestion.hidden,
                searchStyle: TextStyle(fontSize: 18),
                onSuggestionTap: _getInfo,
                searchInputDecoration: InputDecoration(
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(10),
                  ),
                  labelText: "Search",
                  suffixIcon: IconButton(
                      onPressed: () => numController.clear(),
                      icon: Icon(Icons.clear)),
                  contentPadding: EdgeInsets.symmetric(horizontal: 10),
                ),
                maxSuggestionsInViewPort: 5,
                itemHeight: 55,
                suggestionsDecoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(10),
                    border: Border.all(
                        color: Colors.red,
                        style: BorderStyle.solid,
                        width: 2.0)),
                suggestionStyle: TextStyle(fontSize: 18),
                suggestionItemDecoration: BoxDecoration(
                  border: Border(
                      left: BorderSide(width: 10.0, color: Colors.transparent),
                      top: BorderSide(width: 5, color: Colors.transparent),
                      bottom: BorderSide(width: 5, color: Colors.transparent)),
                ),
              ),

Additional context
This is very random

How to add a margin color

Container(
child: SearchField(
controller: dealerarea,
// validator: (value) {
// if (value == null || value.isEmpty) {
// return 'Please select Area';
// }
// return null;
// },
suggestions: area_list
.map(
(String) => SearchFieldListItem(String,
child: Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
String,
style: const TextStyle(color: Colors.black),
),
),
)),
)
.toList(),
// suggestionState: Suggestion.expand,

                onSuggestionTap: (x) {
                  FocusScope.of(context).unfocus();
                  print(dealerarea.text);

                  for (int i = 0; i < territory.length; i++) {
                    if (territory[i].contains(dealerarea.text)) {
                      setState(() {
                        districts.text = territory[i][1];
                        dealerstate.text = territory[i][2];
                      });
                    }
                  }
                  postal_code(
                      dealerarea.text, districts.text, dealerstate.text);
                },
                textInputAction: TextInputAction.next,
                hasOverlay: false,
                searchStyle: TextStyle(
                  fontSize: 15,
                  color: Colors.black.withOpacity(0.8),
                ),
                searchInputDecoration: const InputDecoration(
                    border: OutlineInputBorder(),
                    focusedBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.all(Radius.circular(8)),
                      borderSide:
                          BorderSide(color: Color(0xFFEB455F), width: 2.0),
                    ),
                    labelText: "Area",
                    hintText: "Select Area"),
              )),

inputFormatter typo in the documentation

Describe the bug
In the documentation on properties, the inputFormatter is listed, however when trying to add the inputFormatter as a property it errors as it doesn't exist. Also I can't find it in the code behind the SearchField widget.

To Reproduce
Steps to reproduce the behavior:

  1. Create search field widget in ui
  2. Try to add inputFormatters as property

Expected behavior
inputFormatter be able to accept inputFormatters properties as listed in the documentation.

Actual behavior
an error is received that the property doesn't exist.

Screenshots
If applicable, add screenshots to help explain your problem.
Screen Shot 2022-06-16 at 8 53 36 PM

Thanks in advance for any help you can provide.

Ignore special characters (/*$-)

Say I have suggestions "1 / 2" and "3 / 4 - 5", regardless if user types "1 2" or "1 / 2", I would like the result to show up. How do I do this?

Remove unnecessary box underneath the suggestion box.

Currently the SearchField widget enables us to modify the suggestion box decoration through the suggestionsDecoration parameter. However, when adding rounded corners to that box, there is still a white box underneath that makes the look really awkward. It can be seen in the next picture, the black lines at the bottom of the suggestion box (which is the black box) shouldn't stop before the black box.

Capture d’écran 2022-08-09 à 15 18 42

This makes me believe that the suggestion box is somehow the child of a container that has a white fillColor or something like that.

Would it be possible to remove that unnecessary box?

custom Y axis between search box and item box, current default is due to itemHeight

I created a search widget like below with overlay enabled:
image

I changed itemHeight to 60 and a "transparent box" exist between my search box and item lists below.
image

And when i changed itemHeight to 100. This y axis between 2 is increased too.
image

I hope that i can remove or custom this "y transparent axis" in my project. Thank you all so much.

Suggestions remain after leaving page/screen

Suggestions Box remains after leaving the screen
When "hasOverlay" is true (default) and the user does not select an item, but leaves the screen (pop), the Suggestion Box remains on screen.

Expected behavior
Suggestion Box to be removed.

Actual behavior
Suggestion Box remains on screen.
What you actually saw instead of the expected behavior.

Screenshots
image

Code sample

SearchField<ToadEstablishment>(
            hasOverlay: true,
            suggestions: establishments
                .map(
                  (e) => SearchFieldListItem<ToadEstablishment>(
                    (e.name ?? ''),
                    item: e,
                  ),
                )
                .toList(),
            suggestionState: Suggestion.expand,
            textInputAction: TextInputAction.next,
            controller: tecSourceSearch,
            focusNode: widget.focusNodes['puSearch'],
            searchInputDecoration: InputDecoration(
              contentPadding: Constants.doublePadding,
              border: InputBorder.none,
              hintText: 'Search Establishment',
              suffixIcon: IconButton(
                tooltip: 'Restore',
                icon: const Icon(Icons.restore),
                onPressed: () => tecSourceSearch.cancel(),
              ),
            ),
            maxSuggestionsInViewPort: 4,
            itemHeight: 50,
            onSuggestionTap: (value) {
              setState(() {
                widget.toadItem.establishment = value.item!;
                tecSourcePhone.text = formatPhone(value.item!.phone);
                tecSourceAddress.text = value.item!.formattedAddress!;
                widget.focusNodes['puDriver']?.requestFocus();
              });
            },
            suggestionAction: SuggestionAction.next,
          ),

Additional context
N/A

suggestions do now show after textfieldfocus has changed

The suggestion do only show automatically when the user manually taps on the SearchField, and not when the focus shifts to another one using searchInputAction

I would suggest opening the suggestions in the focusNode listener instead of the onTap property of the textfield, so that the suggestions show on every focus change

Adding Text Alignment and Text style in the suggestion list that is generated.

Is your feature request related to a problem? Please describe.
I want to add consistency to the text styles, in the suggestion box. Currently, it looks like it is fixed.

Describe the solution you'd like
The search field would accept params for the formatting details for the text shown in the suggestion list.

Can't define custom suggestionItemDecoration

Describe the bug
I get this exception when setting the suggestionItemDecoration:

A borderRadius can only be given for a uniform Border

Looking At the source code in suggestionIemDecoration
I see that bottom border is always set even when I define my own decoration since you are using the copyWith function.

To Reproduce
Steps to reproduce the behavior:

  1. Define suggestionItemDecoration like so:
BoxDecoration(
            borderRadius: BorderRadius.circular(8),
            shape: BoxShape.rectangle,
            border: Border.all(color: Colors.transparent, style: BorderStyle.solid, width: 1.0),

Expected behavior
Have a rounded border for SuggestionItems

Actual behavior
An exception of non-uniform border

Code sample

Show code sample
SearchField<String>(
          onTap: (item) {},
          controller: _controller,
          suggestions: ['ABC', 'DEF', 'GHI', 'JKL']
              .map((String e) => SearchFieldListItem<String>(e,
                  child: Text(
                    e,
                    style: Theme.of(context).textTheme.headline2?.copyWith(fontSize: 15),
                  )))
              .toList(),
          hasOverlay: true,
          searchStyle: Theme.of(context).textTheme.headline2?.copyWith(fontSize: 15),
          itemHeight: 45,
          searchInputDecoration: InputDecoration(
            prefixIcon: IconButton(
              onPressed: () {},
              icon: Icon(PortalIcons.location_on),
            ),
            label: Text(
                       labelText,
                       style: Theme.of(context).textTheme.headline2?.copyWith(fontSize: 12),
                       textScaleFactor: context.textScaler,
                  ),
          ),
          suggestionsDecoration: BoxDecoration(
            border: Border.all(),
            borderRadius: BorderRadius.circular(8),
            shape: BoxShape.rectangle,
          ),
          suggestionItemDecoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8),
            shape: BoxShape.rectangle,
            border: Border.all(color: Colors.transparent, style: BorderStyle.solid, width: 1.0),
          ),
        ),

Feature suggestion - generically typed value

Great widget. Currently, this only works with Strings, have you considered adopting the same approach as the standard DropdownButton, whereby each DropdownMenuItem accepts a value of type T and the list is then built with an itemBuilder?

I know this was originally intended as a search field, but it would be nice to present suggestions while preserving some underlying value.

The master project is not compiling anymore

Describe the bug
Since this new version 7.0.5 my project (and also the github example project) is not compiling anymore. I'm getting this 3 errors bellow:

: Error: Method 'insert' cannot be called on 'OverlayState?' because it is potentially null.
package:searchfield/src/searchfield.dart:348
- 'OverlayState' is from 'package:flutter/src/widgets/overlay.dart' ('../../flutter/packages/flutter/lib/src/widgets/overlay.dart').
package:flutter/…/widgets/overlay.dart:1
Try calling using ?. instead.
          Overlay.of(context).insert(_overlayEntry!);
                              ^^^^^^

: Error: No named parameter with the name 'padding'.
package:searchfield/src/searchfield.dart:532
                padding: EdgeInsets.zero,
                ^^^^^^^
: Context: Found this candidate, but the arguments don't match.
../…/widgets/scrollbar.dart:917

  const RawScrollbar({
        ^^^^^^^^^^^^
: Error: The method 'TextFieldTapRegion' isn't defined for the class '_SearchFieldState<T>'.
package:searchfield/src/searchfield.dart:448
- '_SearchFieldState' is from 'package:searchfield/src/searchfield.dart' ('../lib/src/searchfield.dart').
package:searchfield/src/searchfield.dart:1
Try correcting the name to the name of an existing method, or defining a method named 'TextFieldTapRegion'.

            itemBuilder: (context, index) => TextFieldTapRegion(
            

To Reproduce
Just download the master code on Github and try to compile

Expected behavior
Compile and run

Actual behavior
Not compiling.

Navigator operation requested with a context that does not include a Navigator

Describe the bug
navigator button isnot being with/ through a navigator.
To Reproduce

Steps to reproduce the behavior:

  1. run the app. Go to 'the search icon , top bar.'
  2. Go to 'the search icon, beside message icon , top bar.'Click on 'it'
  3. Scroll down through 'run operations'
  4. See error

Expected behavior
to search with and through the list for a result , "country list"
Actual behavior
error
Screenshots
screenshots below

Code sample

code sample
class Home extends StatefulWidget {

  HomeState createState() => HomeState();
}
class HomeState extends State<Home> {

final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  var dateNow = new DateTime.now();
  Icon customIcon = const Icon(Icons.search);
  Icon customMessages = const Icon(Icons.message);
  Widget customSearchBar = const Text('search');
  List<Map<String, dynamic>> todos = [];
  List<page> display_list = List.from(main_search);
  static List<page> main_search = [];
  void updateList(String value){
  }
  final DescriptionController = TextEditingController();
  int _currentIndex = 3;
  Color butt = Colors.pink;
  @override
  Widget build(BuildContext context) {
    return  DefaultTabController(
      length: 3,
      child: MaterialApp(
        theme: ThemeData(fontFamily: 'Harmattan',),
        home: Scaffold(
          key: _scaffoldKey,
          appBar: AppBar(
    backgroundColor: Colors.lightBlue,
   // elevation: 0,
    title: Text(
    "faceMe",
    style: TextStyle(
    color: Colors.white,
    fontSize: 26,
    fontWeight: FontWeight.bold,
    ),),
            automaticallyImplyLeading: false,
            actions: [
              IconButton(
                **_[onPressed: () {
                  Navigator.of(context).push(MaterialPageRoute(
                      builder: (_) => CountrySearch(
                        title: 'Country List',
                      )));
                },](url)_**
                icon: customIcon,
                color: Colors.white,
              ),
              IconButton(
                onPressed: () {
                const chatroom();
                },
                icon: customMessages,
             color: Colors.white, )
            ],
            centerTitle: true,
          ),
          body: ListView(
            children: [
              SingleChildScrollView(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    StatusSection(),
                    Divider(
                      thickness: 1,
                      color: Colors.grey[300],
                    ),
                    HeaderButtonSection(
                        buttonOne: headerbutton(
                            buttontext: "Live",
                            buttonicon: Icons.video_call,
                            buttonaction: () {

                            },
                            buttoncolor: Colors.red),
                        buttonTwo: headerbutton(
                            buttontext: "Photos",
                            buttonicon: Icons.photo_library,
                            buttonaction: () {

                            },
                            buttoncolor: Colors.green),
                        buttonThree: headerbutton(
                            buttontext: "Room",
                            buttonicon: Icons.video_call,
                            buttonaction: () {
                              const   record();
                            },
                            buttoncolor: Colors.purple)),
                    Divider(
                      thickness: 10,
                      color: Colors.grey[300],
                    ),
                    Room(),
                    Divider(
                      thickness: 10,
                      color: Colors.grey[300],
                    ),
                    StorySection(),
                    Divider(
                      thickness: 10,
                      color: Colors.grey[300],
                    ),
                    PostCard(
                      varifiedpost: true,
                      pics: ylw,
                      name: "Spiderman",
                      time: "4h",
                      postImage: bat,
                      postTitle: "hay gorgeous",
                      like: "15K",
                      comments: "2K",
                      share: "432",
                    ),
                    Divider(
                      thickness: 10,
                      color: Colors.grey[300],
                    ),
                    PostCard(
                      varifiedpost: true,
                      pics: bat,
                      name: "Batman",
                      time: "8h",
                      postImage: sup,
                      postTitle: "yoo bud",
                      like: "30K",
                      comments: "2K",
                      share: "344",
                    ),
                    Divider(
                      thickness: 10,
                      color: Colors.grey[300],
                    ),
                    PostCard(
                      varifiedpost: true,
                      pics: red,
                      name: "Scarlett",
                      time: "4h",
                      postImage: bat,
                      postTitle: " I am scarlett,",
                      like: "15K",
                      comments: "2K",
                      share: "232",
                    ),
                    Divider(
                      thickness: 10,
                      color: Colors.grey[300],
                    ),
                    SuggestionSection(),
                    Divider(
                      thickness: 10,
                      color: Colors.grey[300],
                    ),
                    PostCard(
                      varifiedpost: true,
                      pics: haf,
                      name: "Batman",
                      time: "8h",
                      postImage: sup,
                      postTitle:
                      "yooo dude how are you man..",
                      like: "15K",
                      comments: "2K",
                      share: "211",
                    ),
                  ],
          ),
              ),
      ],),
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: _currentIndex,
            items: [
              BottomNavigationBarItem(
                  icon: Icon(Icons.home, color: Colors.blueAccent),
                  label: "",
                  backgroundColor: Colors.white),
              BottomNavigationBarItem(
                  icon: Icon(Icons.person, color: Colors.grey[700]),
                  label: "",
                  backgroundColor: Colors.white),
              BottomNavigationBarItem(
                  icon: Icon(Icons.settings, color: Colors.grey[700]),
                  label: "",
                  backgroundColor: Colors.white),
              BottomNavigationBarItem(
                  icon: Icon(Icons.notifications, color: Colors.grey[700]),
                  label: "",
                  backgroundColor: Colors.white),
              BottomNavigationBarItem(
                  icon: Icon(Icons.more_vert_outlined, color: Colors.grey[700]),
                  label: "",
                  backgroundColor: Colors.white),
            ],
            onTap: (index) {
              setState(() {
                _currentIndex = index;
              });
            },
          ),
         ),
      ),

    );
  }
}

class record extends StatefulWidget {
  const record({Key? key}) : super(key: key);

  @override
  State<record> createState() => _recordState();
}

class _recordState extends State<record> {
  @override
  Widget build(BuildContext context) {
    return ListWheelScrollView(itemExtent: 2, children: [
      Container(
        child: Image (image: AssetImage("assetsm1.jpg/"),
        ),
      ),
      Divider(
        thickness: 10,
        color: Colors.black,
      ),
  
      Container(
![git hub](https://user-images.githubusercontent.com/38753427/199269151-73a58b2a-43b9-430b-b789-ceeb3ef0097c.jpg)
![github](https://user-images.githubusercontent.com/38753427/199269160-a55f7579-1433-4c10-a12c-42792c35299e.jpg)

    child: Image (image: AssetImage("assetsw1.jpg/"),
       ),
      ),
      Divider(
        thickness: 10,
        color: Colors.black,
      ),
    ]);
  }
}

git hub
github
Additional context
Add any other context about the problem here.

Suggestions list not updating with state

Describe the bug
I create a SearchField like below:

SearchField<Location?>(
  suggestions: widget.searchResults
      .map((e) => SearchFieldListItem<Location?>(
          e?.locationName?.text ?? '',
          child: SearchResultItem(
            item: e,
          )))
      .toList(),
  emptyWidget: const Text("Searching for results"),
  focusNode: _focusNode,
  textInputAction: TextInputAction.next,
  hint: 'Location name',
  searchStyle: TextStyle(
    fontSize: 18,
    color: Colors.black.withOpacity(0.8),
  ),
  searchInputDecoration: InputDecoration(
    focusedBorder: OutlineInputBorder(
      borderSide: BorderSide(
        color: Colors.black.withOpacity(0.8),
      ),
    ),
    border: const OutlineInputBorder(
      borderSide: BorderSide(color: Colors.red),
    ),
  ),
  maxSuggestionsInViewPort: 6,
  onSuggestionTap: (x) {
    print("Suggestion tap");
    print(x.item);
  },
  itemHeight: 50,
  controller: _controller,
),

The suggestions are passed from the parent widget, down to the widget that contains the SearchField.
The suggestions are initally an empty list [] and get updated once you enter something in the textFormField.
The parent widget is a ReduxConsumer.
Let's assume that when you enter more than 3 letters a network request is triggered which when finished, will populate the state with the result.

The problem is that even though widget.searchResults is not empty, the SearchField doesn't update it's state and expand the suggestions... It stays with the emptyWidget...
But if I click outside and then again in the widget, I get suggestions...

I even tried to focusNode.requestFocus() when I have searchResults but nothing really changes...

To Reproduce
Steps to reproduce the behavior:

  1. Create a SearchField that has some asyncronously populated searchResults
  2. Write in the textField something
  3. When more that 3 characters written, network request triggered
  4. On response, populate state

Expected behavior
I would expect that once my state gets updated with some suggestions, the widget should update also...

Actual behavior
The widget remains unchanged as if it's still empty

Dispose on tap outiside

Is there a way to dispose results on tap outside ?something barrier dismissible in alert dialog.

[Proposal] Dynamic Item height

Is your feature request related to a problem? Please describe.
See this example: Image link
As you can see, the string is divided using \n to form multiple lines. The string can have no breaks or up to 5-7 breaks. Right now I am putting empty dashes in new line to fill up the space.

Describe the solution you'd like
Can we have dynamic itemHeight Like if I did not specify the height, it should be dynamic

This widget has been unmounted, so the State no longer has a context (and should be considered defunct).

Hi.

I have a ListView.builder that i use to build a list of search fields. when i add several fields, and add information in one of them
and then try to remove one of them from the list i get "This widget has been unmounted, so the State no longer has a context (and should be considered defunct)."

so if i understand correctly, since the exception shows the searchfield component i assume it's related to this component, that somewhere in the code there is a setState() function called without checking if it's mounted.

first of all.. am i correct assuming that? and is there a work-around for now? thanks

this is the full exception:

======== Exception caught by widgets library =======================================================
The following assertion was thrown building StreamBuilder<List<String?>?>(dirty, state: _StreamBuilderBaseState<List<String?>?, AsyncSnapshot<List<String?>?>>#19e19):
This widget has been unmounted, so the State no longer has a context (and should be considered defunct). 

Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.
The relevant error-causing widget was: 
  MaterialApp MaterialApp:file:///C:/Users/attia/Documents/appointments/appointments-client/lib/main.dart:52:12
When the exception was thrown, this was the stack: 
#0      State.context.<anonymous closure> (package:flutter/src/widgets/framework.dart:942:9)
#1      State.context (package:flutter/src/widgets/framework.dart:948:6)
#2      _SearchFieldState.getYOffset (package:searchfield/src/searchfield.dart:371:32)
#3      _SearchFieldState._createOverlay.<anonymous closure>.<anonymous closure> (package:searchfield/src/searchfield.dart:404:29)
#4      StreamBuilder.build (package:flutter/src/widgets/async.dart:442:81)
#5      _StreamBuilderBaseState.build (package:flutter/src/widgets/async.dart:124:48)
#6      StatefulElement.build (package:flutter/src/widgets/framework.dart:4870:27)
#7      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4754:15)
#8      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#9      Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#10     StatefulElement.update (package:flutter/src/widgets/framework.dart:4960:5)
#11     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#12     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#13     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#14     ProxyElement.update (package:flutter/src/widgets/framework.dart:5108:5)
#15     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#16     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#17     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#18     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#19     StatefulElement.update (package:flutter/src/widgets/framework.dart:4960:5)
#20     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#21     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#22     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#23     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#24     StatefulElement.update (package:flutter/src/widgets/framework.dart:4960:5)
#25     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#26     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5845:32)
#27     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6445:17)
#28     Element.updateChild (package:flutter/src/widgets/framework.dart:3501:15)
#29     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4780:16)
#30     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4928:11)
#31     Element.rebuild (package:flutter/src/widgets/framework.dart:4477:5)
#32     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#33     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#34     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#35     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#36     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#37     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995:5)
#41     _invoke (dart:ui/hooks.dart:151:10)
#42     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#43     _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
====================================================================================================

Small-screen phones bug

At the small-screen phones (in my case it's Nexus 4), search suggestions with 1 result, it has a bug like the screenshot I attached. The search suggestion can't display properly.
image

flutter 3.0.0 update issue

/C:/flutter/.pub-cache/hosted/pub.dartlang.org/searchfield-0.6.4/lib/src/searchfield.dart:314:20: Warning: Operand of null-aware operation '!' has type 'WidgetsBinding' which excludes null.

  • 'WidgetsBinding' is from 'package:flutter/src/widgets/binding.dart' ('/C:/flutter/packages/flutter/lib/src/widgets/binding.dart').
    package:flutter/…/widgets/binding.dart:1
    WidgetsBinding.instance!.addPostFrameCallback((_) {

                 ^
    

[Feature] Add functionality to do validation

Description

As of now, if user don't select any value from the given suggestions, it's accepted as a valid value. For instance, if the drop down is about states in India, and if enter xyz, it would consider a valid value for the state.

The idea is to add a validation property and use TextFormField instead of TextField. After addition if developer wraps SearchField with Form and passes a validator, it will help to validate the input.

Cleaning input

Hi

It is awesome. Is there any way to clean input after suggestion tap?

Height property for search field

Hi there,

Great package, a great time saver.

It's quite frustrating though that the search field's height can not be customised. Would be nice to be able to constrain the height of the textfield at will, a possible way to go about it is wrapping the textfield with a SizeBox and exposing the height property.

Suggestions not displaying on release apk but on debug mode

Describe the bug
The bug is suggestions are not displaying on release mode but on debug mode they do.

To Reproduce

  1. Build apk with "flutter build apk"
  2. Install "app-release.apk" on phone and open it. You will see that searchField called "Tipo de cirugía" won't open suggestions.
    Expected behavior
    I expect that when clicking the searchField, siggestions are displayed.

Actual behavior
Suggestions are not showing up.

Screenshots

Debug Release
image image

Code sample

Padding(
  padding: EdgeInsets.symmetric(vertical: 4.0),
  child: SearchField(
    validator: (value) {
      if (value == null || value.isEmpty) {
        return 'Ingrese el tipo de cirugía';
      }
      return null;
    },
    controller: surgeryController,
    onSuggestionTap: (value) {
      setState(() {
        FocusScope.of(context).unfocus(); // Hide keyboard
      });
    },
    searchInputDecoration: InputDecoration(
        prefixIcon: Icon(MdiIcons.clipboardPulse),
        suffixIcon: IconButton(
          onPressed: () {
            setState(() {
              surgeryController.clear();
            });
          },
          icon: Icon(MdiIcons.closeThick),
        ),
        border: OutlineInputBorder(),
        labelText: 'Tipo de cirugía'),
    suggestions: [
      'Sustitución de válvula aórtica',
      'Revascularización',
      'Comunicación interventrucular',
      'Comunicación interauricular',
      'Sustitución de válvula mitral',
      'Marcapasos'
    ]
        .map((e) => SearchFieldListItem(e, child: Text(e)))
        .toList(),
  ),
),

[Web]`onSuggestionTap` not triggered on flutter stable 3.7

Hi, 2 days ago the flutter team upgrade flutter to version 3.7, I don't know the reason but the onSuggestionTap function is not being triggered when we do tap on a suggestion. I am not sure about the origin of the problem to fix it, we have an app in production using devops so it automatically was build in flutter 3.7 yesterday and now our clients can't use our app.

Thanks.

Toggling enabled in runtime throws exception `_overlay != null`

Describe the bug

  • Toggle the enabled property

To Reproduce

Steps to reproduce the behavior:

  1. Run the example app
  2. Toggle enabled property and hot reload

Expected behavior
No exception is thrown.

Actual behavior
The exception is reported, see the logs for details.

Code sample

logs
mahesh@Maheshs-MacBook-Air-M1 example % flutter run
Could not load custom device from config index 0: Expected enabled to be a boolean.
Launching lib/main.dart on iPhone 12 Pro in debug mode...
Running Xcode build...                                                  
 └─Compiling, linking and signing...                         4.9s
Xcode build done.                                           14.8s
Syncing files to device iPhone 12 Pro...                           118ms

Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

💪 Running with sound null safety 💪

An Observatory debugger and profiler on iPhone 12 Pro is available at: http://127.0.0.1:53546/qi63t2tPiec=/
The Flutter DevTools debugger and profiler on iPhone 12 Pro is available at:
http://127.0.0.1:9101?uri=http://127.0.0.1:53546/qi63t2tPiec=/

Performing hot reload...                                                
Reloaded 1 of 600 libraries in 231ms (compile: 41 ms, reload: 85 ms, reassemble: 82 ms).


══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
The following assertion was thrown while dispatching notifications for FocusNode:
'package:flutter/src/widgets/overlay.dart': Failed assertion: line 161 pos 12: '_overlay != null':
is not true.

When the exception was thrown, this was the stack:
#2      OverlayEntry.remove (package:flutter/src/widgets/overlay.dart:161:12)
#3      _SearchFieldState.initialize.<anonymous closure> (package:searchfield/src/searchfield.dart:343:25)
#4      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:351:24)
#5      FocusNode._notify (package:flutter/src/widgets/focus_manager.dart:1038:5)
#6      FocusManager._applyFocusChange (package:flutter/src/widgets/focus_manager.dart:1804:12)
(elided 4 frames from class _AssertionError and dart:async)

The FocusNode sending notification was:
  FocusNode#b85c5
════════════════════════════════════════════════════════════════════════════════════════════════════
Performing hot reload...                                                
Reloaded 1 of 600 libraries in 190ms (compile: 17 ms, reload: 65 ms, reassemble: 98 ms).

validation for Class model

How can I validate this? also can you add validation to your example county_search.dart sample.

SearchField(
                    focusNode: focus,
                    suggestions: countries
                        .map((country) =>
                            SearchFieldListItem(country.name, item: country))
                        .toList(),
                    suggestionState: Suggestion.expand,
                    controller: _searchController,
                    hint: 'Search by country name',
                    maxSuggestionsInViewPort: 4,
                 
                    itemHeight: 45,
                    inputType: TextInputType.text,
                    onSuggestionTap: (SearchFieldListItem<Country> x) {
                      setState(() {
                        _selectedCountry = x.item!;
                      });
                      focus.unfocus();
                    },
                  ),

Support dynamic positioning of suggestions

Searchfield suggestions should be placed dynamically below or above the SearchInput based on the available space.

Describe the solution you'd like
Add a new enum SuggestionDirection.flex(default)

Remove hasOverlay property

Describe the bug
Now that Searchfield supports positioning the suggestions with an offset and the suggestionDirection, the hasOverlay property is no longer required, This issue tracks a request to remove the overlay property.

Set State call On Suggestion Tap

I am trying to call a setState function On Suggestion Tap, but it prevents suggestion overly closing. Any suggestion to get it done? TIA

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.